593 lines
22 KiB
Markdown
593 lines
22 KiB
Markdown
# API
|
|
|
|
The library exports the surface below from the `sourcedock.dev/petrbalvin/interpres/v2`
|
|
package. The snippets assume:
|
|
|
|
```go
|
|
import "sourcedock.dev/petrbalvin/interpres/v2"
|
|
```
|
|
|
|
The parser implements TOML 1.1: date-times and times without seconds, the
|
|
`\e` and `\xHH` escape sequences, and multi-line inline tables with comments
|
|
and trailing commas. The encoder emits TOML 1.1.
|
|
|
|
## Functions
|
|
|
|
### `func Parse(data []byte) (map[string]any, error)`
|
|
|
|
Decodes a TOML document into an untyped tree, using the value mapping in the
|
|
[Decoding](#decoding) section below. Returns `*SyntaxError` on a malformed
|
|
document. Input that is not valid UTF-8 is rejected before the parser runs.
|
|
Equivalent to `ParseContext(context.Background(), data)`.
|
|
|
|
```go
|
|
tree, err := interpres.Parse([]byte("title = \"x\"\nport = 8080\n"))
|
|
```
|
|
|
|
### `func ParseContext(ctx context.Context, data []byte) (map[string]any, error)`
|
|
|
|
The cancellable variant of `Parse`. An already-cancelled context returns
|
|
`ctx.Err()` before any work. During parsing the context is checked every 64
|
|
top-level statements, so a long document aborts without running to completion.
|
|
|
|
### `func Unmarshal(data []byte, v any) error`
|
|
|
|
Parses `data` and stores the result in the value pointed to by `v`, typically a
|
|
pointer to a struct or to `map[string]any`. Equivalent to
|
|
`UnmarshalContext(context.Background(), data, v)`.
|
|
|
|
```go
|
|
var cfg Config
|
|
if err := interpres.Unmarshal(data, &cfg); err != nil {
|
|
return err
|
|
}
|
|
```
|
|
|
|
### `func UnmarshalContext(ctx context.Context, data []byte, v any) error`
|
|
|
|
The cancellable variant of `Unmarshal`.
|
|
|
|
### `func Marshal(v any) ([]byte, error)`
|
|
|
|
Encodes a `struct` or `map[string]V` value, or a non-nil pointer to one, into a
|
|
TOML document. The emission rules are in the [Encoding](#encoding) section
|
|
below. Equivalent to `MarshalContext(context.Background(), v)`.
|
|
|
|
```go
|
|
out, err := interpres.Marshal(cfg)
|
|
```
|
|
|
|
### `func MarshalContext(ctx context.Context, v any) ([]byte, error)`
|
|
|
|
The cancellable variant of `Marshal`. The context is checked before any work
|
|
and every 64 fields during the reflection walk.
|
|
|
|
## Decoding
|
|
|
|
### Value mapping
|
|
|
|
`Parse` and `Unmarshal` map TOML values to Go types as follows:
|
|
|
|
| TOML value | Go type in the parsed tree |
|
|
|---|---|
|
|
| string | `string` |
|
|
| integer | `int64` |
|
|
| float | `float64` |
|
|
| boolean | `bool` |
|
|
| offset date-time | `time.Time` |
|
|
| local date-time | `LocalDateTime` |
|
|
| local date | `LocalDate` |
|
|
| local time | `LocalTime` |
|
|
| array | `[]any` |
|
|
| table, inline table | `map[string]any` |
|
|
| array of tables | `[]map[string]any` |
|
|
|
|
When decoding into a struct, these values convert onto the destination's
|
|
concrete types: any integer or unsigned width, floats, slices, nested structs
|
|
and `map[string]T`.
|
|
|
|
### Target constraints
|
|
|
|
`Unmarshal` and `(*Decoder).Decode` write into a non-nil pointer:
|
|
|
|
- `*struct`, matched per the field rules below
|
|
- `*map[string]any` or `*map[string]T`, keys become map keys and values decode
|
|
into `T` recursively
|
|
- `*any`, receives the whole parsed tree unchanged
|
|
|
|
Anything else returns `interpres: decode target must be a non-nil pointer`.
|
|
|
|
### Field matching
|
|
|
|
For a struct destination, a TOML key matches a field as follows:
|
|
|
|
1. The `toml:"name"` tag, using the part before any comma. The literal `-`
|
|
excludes the field.
|
|
2. Without a tag, the lower-cased field name.
|
|
3. An anonymous (embedded) field without a tag is inlined: the decoder walks
|
|
into the embedded struct and matches its own fields against the same keys,
|
|
mirroring how the encoder flattens it. A nil embedded pointer struct is
|
|
allocated on demand. An untagged embedded map receives the keys no field
|
|
claims.
|
|
4. The key itself is lower-cased before lookup, so the match is
|
|
case-insensitive on both sides: `DATABASEURL` matches a field named
|
|
`DatabaseUrl`.
|
|
|
|
The match is exact after lower-casing. No separator is inserted, so a TOML key
|
|
`database_url` does not match a field named `DatabaseUrl`; tag such a field
|
|
(`toml:"database_url"`) or use the lower-cased name as the key. When two
|
|
fields resolve to the same name, the shallower one wins; at equal depth, the
|
|
one declared later wins.
|
|
|
|
Unknown keys are ignored by default, landing in an untagged embedded map when
|
|
the struct has one; [Strict decoding](#strict-decoding) rejects them instead.
|
|
|
|
### Numeric conversion
|
|
|
|
The parser produces `int64` for every integer and `float64` for every float.
|
|
The decoder converts to the destination type with explicit overflow checks:
|
|
|
|
| Destination kind | Rule |
|
|
|---|---|
|
|
| `int`, `int8`, `int16`, `int32`, `int64` | the `int64` value must not overflow the destination |
|
|
| `uint`, `uint8`, `uint16`, `uint32`, `uint64` | the value must be non-negative and must not overflow the destination's own width, `uint` on a 32-bit platform included; `uint64` accepts any non-negative `int64` |
|
|
| `float32`, `float64` | copied verbatim, except that a finite value beyond the `float32` range is an overflow error rather than a silent infinity; an integer also coerces, so TOML `5` decodes into `5.0` |
|
|
| `bool`, `string` | exact kind match only, no coercion across kinds |
|
|
| `time.Time` | offset date-times only; no implicit conversion to or from the local variants |
|
|
|
|
A conversion that the rules do not allow produces an error wrapped with the
|
|
offending key or index, for example `p: interpres: integer 300 overflows uint8`.
|
|
|
|
### Date-time values
|
|
|
|
Offset date-times decode into `time.Time` and keep their offset. The local
|
|
variants decode into `LocalDateTime`, `LocalDate` and `LocalTime`, whose
|
|
embedded `time.Time` is normalised to UTC (midnight UTC for a local date, the
|
|
zero date for a local time). Every kind may omit the seconds as of TOML 1.1
|
|
(`07:32`, `1979-05-27T07:32`); such a value carries a zero second, and the
|
|
encoder writes the seconds only when the value carries them, so a document
|
|
written without seconds comes back without them. There is no implicit
|
|
conversion between the offset and local kinds; assigning one to the other is an
|
|
error. The four types take a bare timestamp and never a quoted string, so a
|
|
document that writes a date-time with quotes does not decode into them, and
|
|
neither `encoding.TextUnmarshaler` nor the embedded `time.Time` changes that.
|
|
|
|
### Arrays of tables
|
|
|
|
A `[[a]]` block parses into a `[]map[string]any` element of the tree. When the
|
|
destination is a slice, each element decodes into the slice's element type
|
|
(`[]struct` or `[]map[string]V`); a mismatch on one element surfaces as an
|
|
error wrapped with `[i]:` and the element index.
|
|
|
|
### Custom decoding: `Unmarshaler`
|
|
|
|
A type that wants full control of its decode implements:
|
|
|
|
```go
|
|
type Unmarshaler interface {
|
|
UnmarshalTOML(data any) error
|
|
}
|
|
```
|
|
|
|
`data` is whatever the parser produced for that key: `string`, `bool`, `int64`,
|
|
`float64`, `time.Time`, `LocalDateTime`, `LocalDate`, `LocalTime`, `[]any`, or
|
|
`map[string]any`. The method inspects the value and mutates its own receiver;
|
|
the decoder keeps whatever state the receiver stored.
|
|
|
|
The method is usually on a pointer receiver (`*T`). The decoder invokes it when
|
|
the destination type or its pointer implements the interface, so a
|
|
pointer-receiver implementation on an addressable struct field is found
|
|
automatically, and a nil pointer destination is allocated first. An error
|
|
returned from `UnmarshalTOML` halts the decode and propagates wrapped with the
|
|
key path, for example `addr: unmarshal: not a string`.
|
|
|
|
### Custom decoding: `encoding.TextUnmarshaler`
|
|
|
|
A destination type that implements `encoding.TextUnmarshaler` receives a TOML
|
|
string as its text content, the rule `encoding/json` follows:
|
|
|
|
```go
|
|
func (ip *IP) UnmarshalText(text []byte) error
|
|
```
|
|
|
|
The decoder looks for the method on the destination and on its address, so a
|
|
pointer-receiver `UnmarshalText` is invoked on an addressable struct field, and
|
|
the elements of a slice destination are reached the same way. The text path
|
|
applies to TOML strings only: every other value kind keeps its own rule, so
|
|
`r = 1` does not reach a receiver that expects text. An error from
|
|
`UnmarshalText` halts the decode and propagates with the key path and the
|
|
prefix `unmarshal text:`, for example `addr: unmarshal text: not an address`.
|
|
|
|
[`UnmarshalTOML`](#custom-decoding-unmarshaler) wins over `UnmarshalText` when
|
|
a type implements both, and the four [date-time
|
|
types](#date-time-values) are excluded: a quoted string stays a string and
|
|
never becomes a `time.Time` or one of the local wrappers.
|
|
|
|
### Durations
|
|
|
|
TOML has no duration type, so `time.Duration` has a rule of its own. The
|
|
encoder writes the canonical Go form in a TOML string, `1h30m0s`, and the
|
|
decoder reads that string back with `time.ParseDuration`. A bare integer is
|
|
still the nanosecond count it has always been, so `from_int = 5400000000000`
|
|
and `from_text = "1h30m"` decode to the same duration. Text that
|
|
`time.ParseDuration` rejects, `d = "90"` among it, fails with
|
|
`interpres: invalid duration "90"`.
|
|
|
|
### Strict decoding
|
|
|
|
By default unknown keys are dropped silently. A `Decoder` built with
|
|
`DisallowUnknownFields` rejects them instead:
|
|
|
|
```go
|
|
err := interpres.NewDecoder().
|
|
DisallowUnknownFields().
|
|
Decode(data, &cfg)
|
|
```
|
|
|
|
A typo such as `database_urls` then fails with
|
|
`interpres: unknown field "database_urls" for main.Config` instead of a silent
|
|
default-zero run. Strictness applies to every struct the decode reaches, at any
|
|
depth, including struct elements inside slices; map destinations accept every
|
|
key by nature. When several keys are unknown, the message names the smallest
|
|
one, so it does not depend on map iteration order.
|
|
|
|
### Cancellation
|
|
|
|
`ParseContext`, `UnmarshalContext` and `(*Decoder).DecodeContext` accept a
|
|
`context.Context`. An already-cancelled context short-circuits with
|
|
`context.Canceled` before any work begins; afterwards the context is checked
|
|
every 64 top-level statements.
|
|
|
|
### Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Caller
|
|
participant Unmarshal as Unmarshal
|
|
participant Parser as parser
|
|
participant Decoder as decoder
|
|
Caller->>Unmarshal: data, v
|
|
Unmarshal->>Parser: ParseContext(ctx, data)
|
|
Parser-->>Unmarshal: tree or *SyntaxError
|
|
Unmarshal->>Decoder: decode(tree, reflect value)
|
|
Decoder-->>Unmarshal: nil or wrapped field error
|
|
Unmarshal-->>Caller: error
|
|
```
|
|
|
|
## Encoding
|
|
|
|
### Input constraints
|
|
|
|
`Marshal` and `(*Encoder).Marshal` accept a `struct`, a `map[string]V`, or a
|
|
non-nil pointer to one, where `V` is any value `Marshal` itself understands. A
|
|
different top-level value fails:
|
|
|
|
| Input | Error |
|
|
|---|---|
|
|
| a bare scalar or array | `interpres: top-level value must be a struct or map[string]V, got <type>` |
|
|
| a nil `any` | `interpres: cannot marshal nil value` |
|
|
| a nil pointer | `interpres: cannot marshal nil pointer` |
|
|
|
|
### Field matching
|
|
|
|
Struct fields become TOML keys as follows:
|
|
|
|
1. The `toml:"name"` tag, using the part before any comma. The literal `-`
|
|
skips the field.
|
|
2. Without a tag, the lower-cased field name. The key emitted for a field named
|
|
`DatabaseUrl` is `databaseurl`; tag the field to emit `database_url`.
|
|
3. An anonymous (embedded) field without a tag is inlined into the parent
|
|
table; with a tag it is a regular field under that name.
|
|
|
|
Keys that match `[A-Za-z0-9_-]+` are emitted bare, all others quoted. A
|
|
`map[string]V` emits its keys in sorted order for deterministic output, and a
|
|
nil map emits nothing.
|
|
|
|
### Tag options
|
|
|
|
The part of a `toml` tag after the first comma carries options. Both options
|
|
shape emission only; the decoder ignores them.
|
|
|
|
- `omitzero` skips the field when its value is the zero value of its type. A
|
|
type with an `IsZero() bool` method (time.Time among them) decides through
|
|
that method, so a zero `time.Time` or an all-zero struct disappears from
|
|
the output.
|
|
- `omitempty` skips the field when it holds an empty collection: a nil or
|
|
empty slice or array, or a nil or empty map. Strings and other scalars are
|
|
not covered by `omitempty`; use `omitzero` for those.
|
|
|
|
```go
|
|
type Config struct {
|
|
Host string `toml:"host,omitzero"`
|
|
Started time.Time `toml:"started,omitzero"`
|
|
Tags []string `toml:"tags,omitempty"`
|
|
}
|
|
```
|
|
|
|
Options combine after the name: `toml:"name,omitempty,omitzero"` is valid, and
|
|
an unknown option is ignored.
|
|
|
|
Untagged embedded fields round-trip: the decoder inlines embedded structs and
|
|
routes unclaimed keys into an embedded map exactly where the encoder flattened
|
|
them.
|
|
|
|
### Group-by-kind layout
|
|
|
|
By default every table is emitted with its entries grouped by kind:
|
|
|
|
1. scalars (`string`, `int64`, `float64`, `bool`, `time.Time`,
|
|
`LocalDateTime`, `LocalDate`, `LocalTime`)
|
|
2. sub-tables (structs and `map[string]V` values)
|
|
3. arrays of tables (`[]struct` and `[]map[string]V`)
|
|
|
|
Within each group the order follows struct field declaration order, or sorted
|
|
key order for maps. This is the only layout that reliably re-parses to the same
|
|
tree: once a `[header]` is written, later scalars at the parent level would be
|
|
parsed as keys of the sub-table.
|
|
|
|
### Preserving declaration order
|
|
|
|
`GroupByKind(false)` on an `Encoder` walks the entries in declaration order
|
|
instead, emitting each header immediately before its content:
|
|
|
|
```go
|
|
out, err := interpres.NewEncoder().GroupByKind(false).Marshal(cfg)
|
|
```
|
|
|
|
The output remains parseable, but a scalar declared after a sub-table lands
|
|
under that sub-table's header when the document is read back. Use this layout
|
|
for presentation only, not when the output must round-trip.
|
|
|
|
### Custom encoding: `Marshaler`
|
|
|
|
A type that wants a non-default TOML shape implements:
|
|
|
|
```go
|
|
type Marshaler interface {
|
|
MarshalTOML() (any, error)
|
|
}
|
|
```
|
|
|
|
The returned value is encoded as if it had been passed in place of the
|
|
receiver, so it may be a scalar, a slice, an array of tables, or another
|
|
struct or map, including the `Marshaler` result of another type; the encoder
|
|
recurses. An error returned from `MarshalTOML` fails the marshal wrapped with
|
|
the key path, for example `interpres: server.port: bad timestamp`. A result
|
|
of `nil` with a nil error fails the same way with
|
|
`MarshalTOML returned a nil value`: nil has no TOML representation, so
|
|
dropping the field silently is not an option.
|
|
|
|
```go
|
|
type Port int
|
|
|
|
func (p Port) MarshalTOML() (any, error) {
|
|
return int64(p), nil
|
|
}
|
|
```
|
|
|
|
### Custom encoding: `encoding.TextMarshaler`
|
|
|
|
A type that implements `encoding.TextMarshaler` is encoded as a TOML string
|
|
holding the text the method returns, which is the rule `encoding/json` follows:
|
|
|
|
```go
|
|
func (ip IP) MarshalText() ([]byte, error)
|
|
```
|
|
|
|
The encoder looks for the method on the value and on its address, so a
|
|
pointer-receiver `MarshalText` is found on a struct field of an addressable
|
|
value (pass a pointer to `Marshal`) and always on a slice element. `net.IP`,
|
|
`netip.Addr` and user types follow this rule, and a struct that implements the
|
|
interface becomes a string rather than a table. `MarshalTOML` wins when a type
|
|
implements both, the four [date-time types](#date-time-values) keep their bare
|
|
timestamp form, and text that is not valid UTF-8 is an error rather than a
|
|
replacement character.
|
|
|
|
A duration carries no text method of its own; see [Durations](#durations) for
|
|
its rule.
|
|
|
|
### Arrays
|
|
|
|
An array whose every element is a table (`[]struct`, `[]map[string]V`, after
|
|
pointer dereference) emits as an array of tables. TOML also lets one array mix
|
|
tables with scalars; such an array emits as a plain value array, with the
|
|
table elements rendered as inline tables:
|
|
|
|
```go
|
|
tree, _ := interpres.Parse([]byte(`arr = [1, {a = 2}, "x"]`))
|
|
out, _ := interpres.Marshal(tree) // arr = [1, {a = 2}, "x"]
|
|
```
|
|
|
|
A `[]any` holding only tables keeps the value-array form as well, because that
|
|
is the shape `Parse` gives a value array of inline tables; emitting it as
|
|
`[[headers]]` would re-parse as `[]map[string]any` and change the value's type
|
|
across a round-trip.
|
|
|
|
### Empty arrays
|
|
|
|
A nil slice is always omitted. An empty (length 0) array of tables is always
|
|
omitted, because TOML forbids an empty `[[a]]`. Other empty arrays emit as
|
|
`key = []` by default; `OmitEmptyArrays()` skips them as well, so
|
|
`[]string{}` is treated like a nil slice.
|
|
|
|
### Long strings
|
|
|
|
By default every string is emitted as a basic `"..."` string with the escapes
|
|
TOML requires, a newline among them as `\n`. `UseLiteralMultiline(threshold)`
|
|
switches strings that contain a newline and are at least `threshold` bytes long
|
|
to the literal `'''...'''` form, which carries the newlines verbatim:
|
|
|
|
```go
|
|
out, err := interpres.NewEncoder().UseLiteralMultiline(80).Marshal(cfg)
|
|
```
|
|
|
|
Single-line strings keep the basic form regardless of the threshold, and a
|
|
threshold of `0` or less disables the option. A string the literal form cannot
|
|
carry verbatim (an embedded run of three single quotes, a control character
|
|
other than tab or newline, or a carriage return outside a CRLF pair) also keeps
|
|
the basic form, so the output always re-parses to the same value.
|
|
|
|
### Inline tables
|
|
|
|
A table element of a value array is written as one `{a = 1, b = 2}` line while
|
|
it fits. An inline table that would pass the hundredth column carries newlines
|
|
and a trailing comma instead, which TOML 1.1 allows:
|
|
|
|
```toml
|
|
arr = [1, {
|
|
n = 1,
|
|
name = "a value long enough to push the line past the limit",
|
|
}]
|
|
```
|
|
|
|
The closing brace and the entries are indented one tab per nesting level, a
|
|
nested table is measured on its own line, and the output re-parses to the same
|
|
value either way.
|
|
|
|
### Cancellation
|
|
|
|
`MarshalContext` and `(*Encoder).MarshalContext` accept a `context.Context`. The
|
|
context is checked before any work and every 64 fields during the reflection
|
|
walk.
|
|
|
|
### What is not preserved
|
|
|
|
The output is not byte-identical to any document that produced the value:
|
|
|
|
- comments are dropped, and whitespace inside expressions is normalised
|
|
- map keys are emitted in sorted order
|
|
- the choice between `[table]` headers and inline tables is not preserved
|
|
- strings use the basic quoted form unless the literal option above applies
|
|
- a date-time drops its zero seconds and the trailing zeros of its fraction, so
|
|
`07:32:00` is written `07:32`; both are the same value
|
|
- floats always carry a `.` or an exponent, so a float `1` is emitted as `1.0`
|
|
and stays distinguishable from the integer `1` across a round-trip; negative
|
|
zero is normalised to `0.0`
|
|
|
|
The output is guaranteed to re-parse through `Parse` into an equivalent value
|
|
tree. `Marshal` cannot encode cyclic data structures.
|
|
|
|
### Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Caller
|
|
participant Marshal as Marshal
|
|
participant Walk as reflection walk
|
|
participant Emit as emitter
|
|
Caller->>Marshal: v any
|
|
Marshal->>Walk: build tomlDoc from struct or map
|
|
Walk-->>Marshal: tomlDoc or wrapped error
|
|
Marshal->>Emit: emitDoc(doc)
|
|
Emit-->>Marshal: bytes or error
|
|
Marshal-->>Caller: bytes, error
|
|
```
|
|
|
|
## Types
|
|
|
|
### `type SyntaxError struct{ Line int; Msg string }`
|
|
|
|
Describes a malformed TOML document; `Line` is 1-based and `Error()` renders as
|
|
`interpres: line N: msg`. Read the structured fields with a type assertion or
|
|
`errors.AsType`:
|
|
|
|
```go
|
|
if se, ok := errors.AsType[*interpres.SyntaxError](err); ok {
|
|
fmt.Println(se.Line, se.Msg)
|
|
}
|
|
```
|
|
|
|
### `type DecodeError struct{ Path []string; Err error }`
|
|
|
|
Wraps a decoding failure with the key path at which it happened. `Path` lists
|
|
one segment per level from the document root, the outermost key first: a key
|
|
contributes its name, an array element its bracketed index, so the path of the
|
|
`weight` field in the first item reads `["items", "[0]", "weight"]`. The
|
|
rendered message is unchanged by the type; read the fields instead of parsing
|
|
the message:
|
|
|
|
```go
|
|
if de, ok := errors.AsType[*interpres.DecodeError](err); ok {
|
|
fmt.Println(de.Path, de.Err)
|
|
}
|
|
```
|
|
|
|
### `type EncodeError struct{ Path string; Err error }`
|
|
|
|
Wraps an encoding failure with the key path of the value that failed, in the
|
|
document's own notation: `server.ports[2]`. Read it with `errors.AsType` the
|
|
same way.
|
|
|
|
### `type Decoder`
|
|
|
|
Configurable strictness for decoding, constructed with `NewDecoder`. Set up
|
|
with `DisallowUnknownFields`, then call `Decode` or `DecodeContext` any number
|
|
of times. A configured `Decoder` holds no per-call state and is safe for
|
|
concurrent use.
|
|
|
|
### `type Encoder`
|
|
|
|
Configurable emission policy, constructed with `NewEncoder`. The option state
|
|
is private; set it with the chainable methods, each of which returns the
|
|
encoder:
|
|
|
|
| Method | Default | Effect |
|
|
|---|---|---|
|
|
| `GroupByKind(v bool)` | `true` | group entries as scalars, then sub-tables, then arrays of tables; `false` preserves declaration order |
|
|
| `OmitEmptyArrays()` | off | skip `key = []` for empty scalar arrays |
|
|
| `UseLiteralMultiline(threshold int)` | `0` | emit multi-line strings of at least `threshold` bytes as literal `'''...'''` |
|
|
|
|
```go
|
|
out, err := interpres.NewEncoder().
|
|
GroupByKind(false).
|
|
OmitEmptyArrays().
|
|
UseLiteralMultiline(80).
|
|
MarshalContext(ctx, cfg)
|
|
```
|
|
|
|
A configured `Encoder` holds no per-call state; each `Marshal` or
|
|
`MarshalContext` call copies the options and is safe for concurrent use, as
|
|
long as no setter races with a call.
|
|
|
|
### `type Marshaler interface{ MarshalTOML() (any, error) }`
|
|
|
|
See [Custom encoding](#custom-encoding-marshaler).
|
|
|
|
### `type Unmarshaler interface{ UnmarshalTOML(data any) error }`
|
|
|
|
See [Custom decoding](#custom-decoding-unmarshaler).
|
|
|
|
### Date-time wrappers
|
|
|
|
```go
|
|
type LocalDateTime struct{ time.Time } // 1979-05-27T07:32:00
|
|
type LocalDate struct{ time.Time } // 1979-05-27
|
|
type LocalTime struct{ time.Time } // 07:32:00.999999
|
|
```
|
|
|
|
Each carries a `String()` method returning the TOML-canonical rendering: the
|
|
seconds appear only when the value carries them, and a fractional second drops
|
|
its trailing zeros, so `07:32:00` renders as `07:32` and a half second as
|
|
`00.5`. The types are produced by `Parse` and accepted by `Marshal`, which
|
|
writes them through `String()`.
|
|
|
|
## Errors
|
|
|
|
The entry points return:
|
|
|
|
- `*SyntaxError` for a malformed document, with the 1-based line
|
|
- `*DecodeError` for a decoding failure, with the key path in `Path`
|
|
- `*EncodeError` for an encoding failure, with the key path in `Path`
|
|
- a plain error for the rest: a non-pointer decode target, a cancelled
|
|
context, a key that is not valid UTF-8
|
|
|
|
Decode and encode failures carry the key path or element index in the typed
|
|
wrappers above, so `errors.Is` and `errors.AsType` see through them and the
|
|
path reads from a field instead of the message text.
|
|
|
|
## Notes
|
|
|
|
The exported surface is documented in godoc form in the source, and `go doc .`
|
|
run from the module root is the authority on signatures and types. This file
|
|
explains what the surface is for and how the parts fit together.
|