docs: document set and changelog
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# Architecture
|
||||
|
||||
How interpres is put together. Every file, package and arrow below exists in the
|
||||
source tree; nothing is aspirational.
|
||||
|
||||
## Overview
|
||||
|
||||
interpres is one public library package, one command, and one example. The
|
||||
library implements the whole of TOML 1.0, decoding and encoding, in the
|
||||
standard library alone; the command wraps the parser for the toml-test
|
||||
compliance harness, against which it stands at 185 valid and 371 invalid cases
|
||||
with zero failures; the example demonstrates the API.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
CLI[cmd/interpres-decode<br/>toml-test adapter] --> API
|
||||
EX[examples/basic<br/>usage demo] --> API
|
||||
subgraph Lib [package interpres]
|
||||
API[interpres.go<br/>public API and types]
|
||||
API --> P[parser.go<br/>recursive-descent parser]
|
||||
API --> DEC[decode.go<br/>tree onto Go values]
|
||||
API --> ENC[encode.go<br/>Go values to TOML]
|
||||
P --> N[number.go<br/>numeric tokens]
|
||||
P --> DT[datetime.go<br/>date-time atoms and types]
|
||||
end
|
||||
```
|
||||
|
||||
The public API in `interpres.go` is a thin facade: every entry point funnels
|
||||
into `ParseContext` for parsing and into the unexported `decoder` and `encoder`
|
||||
for the reflection work. `parser.go` owns the grammar; it leans on
|
||||
`number.go` and `datetime.go` for the two token families that need their own
|
||||
strict validation.
|
||||
|
||||
## Packages
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `.` (package `interpres`) | The whole library. `interpres.go` declares the exported surface (`Parse`, `Unmarshal`, `Marshal`, the `*Context` variants, `Decoder`, `Encoder`, `Marshaler`, `Unmarshaler`, `SyntaxError`, the local date-time types); everything below it is unexported. |
|
||||
| `cmd/interpres-decode` | The toml-test adapter. Reads TOML on stdin, writes tagged JSON on stdout. Owns no parsing logic. |
|
||||
| `examples/basic` | A runnable tour of the API. Documentation in executable form, not part of the library. |
|
||||
|
||||
Inside the library package, one file owns one concern:
|
||||
|
||||
| File | Responsibility |
|
||||
|---|---|
|
||||
| `parser.go` | The recursive-descent parser. Produces the `map[string]any` tree and enforces the structural rules of TOML 1.0 (table redefinitions, dotted keys, arrays of tables). Reports a 1-based line on failure. |
|
||||
| `number.go` | Strict numeric tokens: integers in the four radixes with `_` separators, and floats including `inf` and `nan`. Rejects leading zeros, misplaced underscores and malformed fractions. |
|
||||
| `datetime.go` | The three local date-time wrapper types and `parseDateTime`, which classifies a token into the four date-time kinds under the strict TOML grammar. |
|
||||
| `decode.go` | Maps the parsed tree onto Go values by reflection: struct fields, maps, slices, scalar conversion with overflow checks, `Unmarshaler` dispatch. |
|
||||
| `encode.go` | The reverse walk: builds an intermediate `tomlDoc` per table (which is what preserves declaration order and enables the group-by-kind partition) and then emits it as TOML. |
|
||||
|
||||
The boundary that matters: `parser.go` produces only untyped trees
|
||||
(`map[string]any`, `[]any`, `[]map[string]any`, scalars); `decode.go` and
|
||||
`encode.go` are the only files that touch `reflect`; the command never touches
|
||||
either, it consumes `Parse` alone.
|
||||
|
||||
## Data flow
|
||||
|
||||
Decoding is parse, then one reflection walk. `SyntaxError` values are produced
|
||||
inside `parser.go` and returned as-is; conversion errors are produced inside
|
||||
`decode.go` and wrapped with the key path as they unwind.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Caller
|
||||
participant API as interpres.go
|
||||
participant P as parser.go
|
||||
participant D as decode.go
|
||||
Caller->>API: Unmarshal(data, v)
|
||||
API->>P: ParseContext(ctx, data)
|
||||
P->>P: number and datetime atoms
|
||||
P-->>API: map tree or *SyntaxError
|
||||
API->>D: decode(tree, reflect value)
|
||||
D-->>API: nil or wrapped field error
|
||||
API-->>Caller: error
|
||||
```
|
||||
|
||||
Encoding walks the other way. `encode.go` first builds a `tomlDoc` from the
|
||||
value, then emits it; the two phases are why `GroupByKind` can reorder entries
|
||||
without a second reflection pass, and why cancellation is checked during both.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Caller
|
||||
participant API as interpres.go
|
||||
participant B as encode.go build
|
||||
participant E as encode.go emit
|
||||
Caller->>API: Marshal(v)
|
||||
API->>B: build tomlDoc from struct or map
|
||||
B-->>API: tomlDoc or error
|
||||
API->>E: emitDoc(doc)
|
||||
E-->>API: bytes or error
|
||||
API-->>Caller: bytes, error
|
||||
```
|
||||
|
||||
## State and lifetime
|
||||
|
||||
- The exported `Decoder` and `Encoder` hold configuration only. Every
|
||||
`Decode`, `DecodeContext`, `Marshal` and `MarshalContext` call allocates its
|
||||
own unexported worker, so a configured type is safe for concurrent use; the
|
||||
setter methods are not, and must finish before the value is shared.
|
||||
- The parser is allocated per `ParseContext` call; nothing is cached between
|
||||
documents.
|
||||
- The date-time wrappers are values, not pointers, and are immutable in use.
|
||||
- Nothing in the library starts goroutines or holds locks; concurrency safety
|
||||
comes from having no shared mutable state.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None. `go.mod` declares the module and the Go version and carries no requires;
|
||||
the library imports the standard library only, which is the point of the
|
||||
project. The `toml-test` binary is a development and CI tool, never a module
|
||||
dependency.
|
||||
Reference in New Issue
Block a user