types

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package types holds framework value types that are shared by generated models, handler DTOs, and the admin data plane. They exist so a single Go type flows through the model, the Huma contract (OpenAPI/TS client), and GORM persistence without the DTO drifting from the model (see #222 / #218).

Index

Constants

View Source
const DecimalPattern = `^-?[0-9]+(\.[0-9]+)?$`

DecimalPattern is the create-body spelling of a decimal. Huma enforces it on the string schema. Bounds and defaults must use the same token so the form, the magnitude check, and the request accept one spelling.

View Source
const MaxDecimalDigits = 1000

MaxDecimalDigits bounds the digits a decimal value may spell: the digits String writes for it, before and after the point ("0.05" is 3). It is far beyond any money column (MySQL's widest is decimal(65,30)) and exists because a decimal's exponent is otherwise unbounded: "1e1000000000" parses in microseconds, but formatting or comparing it materialises every digit and holds a core and gigabytes of memory (issue #440 review).

It is the one size rule for decimals: parsing (UnmarshalJSON, UnmarshalText, NewDecimalFromString), reading (Scan), and the database write guard all judge a value with DecimalShapeOf, so a value one of them accepts the others accept too. A PostgreSQL numeric with no declared precision can hold more; such a value fails to load.

View Source
const TimeOfDayPattern = `^([01][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9](Z|[+-]([01][0-9]|2[0-3]):[0-5][0-9])?)?$`

TimeOfDayPattern is the one clock grammar. Huma's format "time" accepts HH:MM:SS and a numeric offset, and rejects HH:MM. This pattern accepts all three, which is what UnmarshalText parses and stores as HH:MM:SS. The generator, the admin write, and the type all use it. Format "time" is not that grammar.

Variables

This section is empty.

Functions

func CheckDecimalSize added in v0.7.1

func CheckDecimalSize(d decimal.Decimal) error

CheckDecimalSize returns an error when d is beyond the size rule; see DecimalShapeOf.

func CheckDecimalSpelling added in v0.7.1

func CheckDecimalSpelling(s string) error

CheckDecimalSpelling refuses decimal text too long for any value within the size rule, before it is parsed (parsing a long coefficient is quadratic).

func DateWithin added in v0.7.1

func DateWithin(d Date) error

DateWithin reports whether d is a date every supported database can store (DateBounds), with a message for the field otherwise.

func DecimalWithin added in v0.3.0

func DecimalWithin(d Decimal, min, max string) error

DecimalWithin reports whether d is inside the optional bounds. Empty min or max is unbounded on that side. Bounds are decimal magnitudes: Huma's minimum/maximum apply only to number and integer schemas, and Decimal's schema is a string.

func IsPlainDecimalSpelling added in v0.7.1

func IsPlainDecimalSpelling(s string) bool

IsPlainDecimalSpelling reports whether s is a plain decimal spelling: [sign] digits [. digits] [e [sign] digits], with at least one digit before or after the point and an exponent of at most nine digits. That is the grammar SQLite, PostgreSQL and MySQL all read as a number ("5.", ".5", "+.5", "1.e5", "1e+05"). It is narrower than shopspring's parser, which also accepts a sign after a leading point (".-5" is -0.05) that no database reads, so code deciding what a database will store must check this, not only whether shopspring parses it (#440 review, round 6).

func TimeBounds added in v0.7.1

func TimeBounds() (minimum, maximum time.Time)

TimeBounds returns the earliest and latest timestamp TimeWithin accepts: 1000-01-02T00:00:00Z and 9999-12-30T23:59:59Z.

func TimeWithin added in v0.7.1

func TimeWithin(t time.Time) error

TimeWithin reports whether t is a timestamp every supported database can store and return (TimeBounds), with a message for the field otherwise.

Types

type Date added in v0.3.0

type Date struct {
	// contains filtered or unexported fields
}

Date is a calendar date. It is not a timestamp and not a time of day.

JSON and text are "YYYY-MM-DD". GORM's default column type is "date" (Postgres DATE, MySQL DATE, SQLite TEXT affinity). Value returns that calendar day as UTC midnight so PostgreSQL can encode it as a binary date. The zero Date is not a stored value: Value and MarshalJSON reject it. JSON null is a nil *Date.

func DateBounds added in v0.7.1

func DateBounds() (minimum, maximum Date)

DateBounds returns the earliest and latest date DateWithin accepts: 1000-01-01 and 9999-12-31.

func NewDate added in v0.3.0

func NewDate(t time.Time) Date

NewDate returns the calendar date of t in UTC, dropping the clock time.

func ParseDate added in v0.3.0

func ParseDate(s string) (Date, error)

ParseDate parses "YYYY-MM-DD".

func (Date) GormDataType added in v0.3.0

func (Date) GormDataType() string

GormDataType tells GORM the column family when a model field omits an explicit type tag. Generated models also set gorm:"type:date".

func (Date) IsZero added in v0.3.0

func (d Date) IsZero() bool

IsZero reports whether d is the zero date.

func (Date) MarshalJSON added in v0.3.0

func (d Date) MarshalJSON() ([]byte, error)

MarshalJSON encodes the date as "YYYY-MM-DD". The zero date has no JSON form: "0001-01-01" is that zero value, and publishing it would round-trip through Value as an error. JSON null is a nil *Date.

func (Date) MarshalText added in v0.3.0

func (d Date) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*Date) Scan added in v0.3.0

func (d *Date) Scan(src any) error

Scan implements sql.Scanner. Drivers return a time.Time, a date string, or nil.

func (Date) String added in v0.3.0

func (d Date) String() string

String returns "YYYY-MM-DD", or "" for the zero date.

func (Date) Time added in v0.3.0

func (d Date) Time() time.Time

Time returns the date as UTC midnight.

func (*Date) UnmarshalJSON added in v0.3.0

func (d *Date) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts null, "YYYY-MM-DD", or an RFC3339 timestamp (the date part is kept). The timestamp form is what admin coercion produces when it JSON-encodes a time.Time.

func (*Date) UnmarshalText added in v0.3.0

func (d *Date) UnmarshalText(b []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (Date) Value added in v0.3.0

func (d Date) Value() (driver.Value, error)

Value implements driver.Valuer. The stored value is UTC midnight on the calendar day, which is what PostgreSQL's binary date codec accepts. A zero Date is not SQL NULL: NULL is a nil *Date, and the zero value is refused so a required column does not silently insert NULL.

type Decimal

type Decimal struct {
	decimal.Decimal
}

Decimal is a fixed-point decimal for money and other exact numeric values.

It wraps shopspring/decimal.Decimal, so it inherits exact arithmetic, JSON (a quoted string — no float rounding), text (un)marshaling, and the database sql.Scanner / driver.Valuer used by GORM. On top of that it advertises an OpenAPI schema (a string with format "decimal") via huma.SchemaProvider, so the generated OpenAPI document and TypeScript client see a string instead of an opaque object. Use the gorm tag `type:decimal(p,s)` on the model field to pin precision/scale for the migration (gombit make resource emits decimal(19,4) by default).

A database opened with database.Open refuses, with a 422-mapped database.ValidationError, a value its column would not store exactly: one that does not fit decimal(p,s), and on SQLite, which has no fixed-point type, one with more than database.SQLiteDecimalDigits digits. Without a `type:decimal(p,s)` tag (a `precision:`/`scale:` tag does not reach the column for this type) the column is the driver's bare decimal, which on MySQL is DECIMAL(10,0) (whole numbers only), so pin decimal(p,s) for money.

func MustDecimal added in v0.3.0

func MustDecimal(s string) Decimal

MustDecimal parses s and panics if it is not a decimal. Generated mappers use it for a default the generator already validated.

func NewDecimal

func NewDecimal(d decimal.Decimal) Decimal

NewDecimal wraps a shopspring decimal.

func NewDecimalFromString

func NewDecimalFromString(s string) (Decimal, error)

NewDecimalFromString parses a decimal string (e.g. "19.99"), refusing one beyond MaxDecimalDigits.

func (Decimal) GormDataType

func (Decimal) GormDataType() string

GormDataType tells GORM the default column family when a model field omits an explicit `type:` tag. Generated models pin exact precision with `gorm:"type:decimal(19,4)"`, which takes precedence over this default.

func (*Decimal) Scan added in v0.7.1

func (d *Decimal) Scan(value any) error

Scan reads a decimal from the database and refuses one beyond the size rule, so a row the guard would refuse to write (stored before the guard existed, or by raw SQL) fails to load with an error instead of hanging the first formatting of it. A non-finite float is refused too: SQLite stores the literal "1e1000000000" in a decimal column as REAL Inf, and shopspring panics on it.

func (Decimal) Schema

func (Decimal) Schema(huma.Registry) *huma.Schema

Schema implements huma.SchemaProvider so the contract represents a Decimal as a JSON string (matching its MarshalJSON), not a reflected struct.

func (*Decimal) UnmarshalJSON added in v0.7.1

func (d *Decimal) UnmarshalJSON(b []byte) error

UnmarshalJSON parses a decimal and refuses one beyond MaxDecimalDigits before anything formats it. A JSON null leaves d unchanged, as encoding/json's convention (and shopspring's own decoder) has it.

func (*Decimal) UnmarshalText added in v0.7.1

func (d *Decimal) UnmarshalText(b []byte) error

UnmarshalText parses a decimal and refuses one beyond MaxDecimalDigits before anything formats it.

type DecimalShape added in v0.7.1

type DecimalShape struct {
	// Whole is the number of digits before the point, 0 for a value below 1.
	Whole int
	// Frac is the number of digits after the point, trailing zeros dropped.
	Frac int
	// Significant runs from the first non-zero digit to the last digit
	// written, trailing integer zeros included (1000 has 4, 0.0012 has 2).
	Significant int
}

DecimalShape is how a decimal is written, as String writes it.

func DecimalShapeOf added in v0.7.1

func DecimalShapeOf(d decimal.Decimal) (DecimalShape, error)

DecimalShapeOf measures d without formatting it beyond its bounded coefficient, and refuses it when its exponent is beyond ±MaxDecimalDigits or it spells more than MaxDecimalDigits digits.

The exponent is bounded on its own, whatever the coefficient: formatting or comparing a shopspring decimal rescales it, which computes 10^|exponent| before the coefficient matters, so "0e1000000000" costs as much as "1e1000000000" (issue #440 review). The coefficient's size is bounded from its bit length before it is formatted.

func (DecimalShape) Digits added in v0.7.1

func (s DecimalShape) Digits() int

Digits is the number of digits String writes: the leading "0" of a value below 1 included.

type Duration added in v0.3.0

type Duration struct {
	// contains filtered or unexported fields
}

Duration is a span of time. JSON and text use Go's duration syntax ("1h30m", "300ms", "0s", "-1s"), which is what Huma's format "duration" accepts. The stored column is a signed bigint of nanoseconds so SQLite, PostgreSQL, and MySQL sort the same integer. A zero Duration is "0s" and is stored. SQL NULL is a nil *Duration.

func MustDuration added in v0.3.0

func MustDuration(s string) Duration

MustDuration parses s and panics if it is not a duration. Generated mappers use it for a default the generator already validated.

func ParseDuration added in v0.3.0

func ParseDuration(s string) (Duration, error)

ParseDuration parses a Go duration string.

func (Duration) Duration added in v0.3.0

func (d Duration) Duration() time.Duration

Duration returns the span as time.Duration.

func (Duration) GormDataType added in v0.3.0

func (Duration) GormDataType() string

GormDataType tells GORM the column family when a model field omits an explicit type tag. Generated models also set gorm:"type:bigint".

func (Duration) MarshalJSON added in v0.3.0

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON encodes the canonical duration string. Zero is "0s".

func (Duration) MarshalText added in v0.3.0

func (d Duration) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*Duration) Scan added in v0.3.0

func (d *Duration) Scan(src any) error

Scan implements sql.Scanner. Drivers return an integer nanosecond count, or the duration text.

func (Duration) String added in v0.3.0

func (d Duration) String() string

String returns the canonical Go duration spelling. "1h30m" becomes "1h30m0s".

func (*Duration) UnmarshalJSON added in v0.3.0

func (d *Duration) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts null or a duration string.

func (*Duration) UnmarshalText added in v0.3.0

func (d *Duration) UnmarshalText(b []byte) error

UnmarshalText implements encoding.TextUnmarshaler. An empty string is rejected: zero is "0s", and "" is not that span.

func (Duration) Value added in v0.3.0

func (d Duration) Value() (driver.Value, error)

Value implements driver.Valuer. The stored value is nanoseconds.

type File added in v0.7.0

type File string

File is a storage-backed field's value: the key of an object in the application's storage (framework.App.Storage()), never the file's bytes. The column holds the key as a string; an empty File is no file. The generated resource code accepts a key only for an upload that passed the field's policy, returns a file object (key, filename, size, content type, URL) on reads, and stores one record per file (a unique index), so the record owns it. See docs/fields.md.

func (File) IsZero added in v0.7.0

func (f File) IsZero() bool

IsZero reports whether there is no file.

func (File) Key added in v0.7.0

func (f File) Key() string

Key returns the object key ("" for no file).

type Image added in v0.7.0

type Image string

Image is a File whose field accepts images only (detected from their bytes); everything else is as for File.

func (Image) IsZero added in v0.7.0

func (i Image) IsZero() bool

IsZero reports whether there is no image.

func (Image) Key added in v0.7.0

func (i Image) Key() string

Key returns the object key ("" for no image).

type JSON added in v0.3.0

type JSON json.RawMessage

JSON is a JSON object or array stored as text. Null is not a document: UnmarshalJSON and Schema reject it. Optional columns use NullJSON, because Huma validates anyOf before it honors a nullable tag, so one schema cannot both reject null (required) and allow it (optional).

func (JSON) GormDataType added in v0.3.0

func (JSON) GormDataType() string

GormDataType tells GORM the column family when a model field omits an explicit type tag. Generated models also set gorm:"type:text".

func (JSON) MarshalJSON added in v0.3.0

func (j JSON) MarshalJSON() ([]byte, error)

MarshalJSON returns null for a nil document and the raw document otherwise.

func (*JSON) Scan added in v0.3.0

func (j *JSON) Scan(src any) error

Scan implements sql.Scanner.

func (JSON) Schema added in v0.3.0

func (JSON) Schema(huma.Registry) *huma.Schema

Schema implements huma.SchemaProvider. Null is not a member: a required field rejects JSON null. Optional fields use NullJSON.

func (*JSON) UnmarshalJSON added in v0.3.0

func (j *JSON) UnmarshalJSON(b []byte) error

UnmarshalJSON stores a JSON object or array. Null is rejected.

func (JSON) Value added in v0.3.0

func (j JSON) Value() (driver.Value, error)

Value implements driver.Valuer. A nil document is SQL NULL.

type NullJSON added in v0.3.0

type NullJSON JSON

NullJSON is a JSON object, a JSON array, or null. Generated optional json columns use it so a stored NULL validates against the published contract.

func (NullJSON) GormDataType added in v0.3.0

func (NullJSON) GormDataType() string

GormDataType tells GORM the column family when a model field omits an explicit type tag. Generated models also set gorm:"type:text".

func (NullJSON) MarshalJSON added in v0.3.0

func (j NullJSON) MarshalJSON() ([]byte, error)

MarshalJSON returns null for a nil document and the raw document otherwise.

func (*NullJSON) Scan added in v0.3.0

func (j *NullJSON) Scan(src any) error

Scan implements sql.Scanner. SQL NULL clears the value.

func (NullJSON) Schema added in v0.3.0

func (NullJSON) Schema(huma.Registry) *huma.Schema

Schema implements huma.SchemaProvider. Object, array, and null are members.

func (*NullJSON) UnmarshalJSON added in v0.3.0

func (j *NullJSON) UnmarshalJSON(b []byte) error

UnmarshalJSON stores a JSON object or array. Null clears the value.

func (NullJSON) Value added in v0.3.0

func (j NullJSON) Value() (driver.Value, error)

Value implements driver.Valuer. A nil document is SQL NULL.

type TimeOfDay added in v0.3.0

type TimeOfDay struct {
	// contains filtered or unexported fields
}

TimeOfDay is a clock time with no date and no zone. JSON and text are "HH:MM:SS". "HH:MM" and a time with a numeric offset ("15:04:05+07:00", the second form Huma's format "time" accepts) are read as that clock and stored as "HH:MM:SS". The offset is not applied: a time of day has no date to convert.

The zero TimeOfDay is midnight, which is a real clock time, and Value stores it. SQL NULL is a nil *TimeOfDay. The column is char(8) so SQLite, PostgreSQL, and MySQL share one sortable text form.

func MustTimeOfDay added in v0.3.0

func MustTimeOfDay(s string) TimeOfDay

MustTimeOfDay parses s and panics if it is not a clock time. Generated mappers use it for a default the generator already validated.

func ParseTimeOfDay added in v0.3.0

func ParseTimeOfDay(s string) (TimeOfDay, error)

ParseTimeOfDay parses a clock time.

func (TimeOfDay) GormDataType added in v0.3.0

func (TimeOfDay) GormDataType() string

GormDataType tells GORM the column family when a model field omits an explicit type tag. Generated models also set gorm:"type:char(8)".

func (TimeOfDay) MarshalJSON added in v0.3.0

func (t TimeOfDay) MarshalJSON() ([]byte, error)

MarshalJSON encodes the clock as "HH:MM:SS".

func (TimeOfDay) MarshalText added in v0.3.0

func (t TimeOfDay) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*TimeOfDay) Scan added in v0.3.0

func (t *TimeOfDay) Scan(src any) error

Scan implements sql.Scanner.

func (TimeOfDay) String added in v0.3.0

func (t TimeOfDay) String() string

String returns "HH:MM:SS". Midnight is "00:00:00".

func (*TimeOfDay) UnmarshalJSON added in v0.3.0

func (t *TimeOfDay) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts null, "HH:MM:SS", "HH:MM", or a clock with a numeric offset.

func (*TimeOfDay) UnmarshalText added in v0.3.0

func (t *TimeOfDay) UnmarshalText(b []byte) error

UnmarshalText implements encoding.TextUnmarshaler. An empty string is rejected: midnight is "00:00:00", and "" is not that clock. The accepted spellings are TimeOfDayPattern, the same pattern the request and the admin write enforce.

func (TimeOfDay) Value added in v0.3.0

func (t TimeOfDay) Value() (driver.Value, error)

Value implements driver.Valuer. The stored value is the canonical "HH:MM:SS".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL