odin-jsonschema

Implementation of JSON schema for Odin
Log | Files | Refs | LICENSE

README.MD (3436B)


      1 # odin-jsonschema
      2 
      3 Generates Odin type declarations from JSON Schema documents (draft-07 and
      4 2020-12). The generated types parse with `core:encoding/json` — no runtime
      5 library required.
      6 
      7 ## Usage
      8 
      9 ### CLI
     10 
     11 ```sh
     12 make build                              # -> build/jschema
     13 
     14 build/jschema schema.json -o:types.odin # file to file
     15 cat schema.json | build/jschema         # stdin to stdout
     16 build/jschema schema.json -pkg:api -root:Document
     17 ```
     18 
     19 Flags:
     20 
     21 | flag | meaning | default |
     22 |------|---------|---------|
     23 | `-o:<path>` | output file | stdout |
     24 | `-pkg:<name>` | package name of the generated file | `schema` |
     25 | `-root:<name>` | name of the root declaration | `Root` |
     26 
     27 Debug builds (`odin build src/cmd/jschema -debug`) wrap the allocator in
     28 `mem.Tracking_Allocator` and report leaks and bad frees on exit.
     29 
     30 ### Library
     31 
     32 ```odin
     33 import "src/pkg/jschema"
     34 
     35 err := jschema.generate("schema.json", "types.odin")
     36 ```
     37 
     38 `generate_source` is the in-memory variant used for stdin/pipelines. All
     39 intermediate state lives in an internal arena that is freed before returning;
     40 the only caller-facing allocation is the returned source string.
     41 
     42 ## Type mapping
     43 
     44 | schema | Odin |
     45 |--------|------|
     46 | `string` / `integer` / `number` / `boolean` | `string` / `i64` / `f64` / `bool` |
     47 | `object` with `properties` (or `allOf`) | named `struct`, merged across `allOf` and `$ref` |
     48 | `object` with `additionalProperties` / single-pattern `patternProperties` | `map[string]T` |
     49 | `array` | `[]T` |
     50 | optional or nullable field | `Maybe(T)` |
     51 | `enum` of strings that are valid Odin identifiers | named `enum` (variant names match the JSON strings exactly, as required by `core:encoding/json`) |
     52 | `enum` otherwise | `string` |
     53 | `oneOf` / `anyOf` | `union {..}` (a `null` variant folds into `Maybe`/nil) |
     54 | `type: [..]` with several types | `union {..}` |
     55 | no usable constraints | `json.Value` |
     56 | `$ref` | named type; local pointers (`#/$defs/..`, `#/definitions/..`) and relative file refs are resolved |
     57 
     58 Recursive schemas work through slices and maps. A schema that contains itself
     59 *by value* (illegal in Odin, and `core:encoding/json` cannot unmarshal pointer
     60 fields) has the offending property emitted as `json.Value`.
     61 
     62 Known limitations:
     63 
     64 - `core:encoding/json` tries union variants in declaration order and skips
     65   unknown object keys, so for `oneOf` of similar objects the first variant
     66   that parses wins.
     67 - Tuple-form `items` / `prefixItems` degrade to `[]json.Value`.
     68 - `$anchor` and remote (URL) refs are not supported; unresolvable refs are an
     69   error.
     70 
     71 ## Internals
     72 
     73 The schema is stored data-oriented: one flat pool of fixed-size nodes plus
     74 shared side arrays (`props`, `children`, `names`, `strings`), all cross-linked
     75 by `u32` indices. Documents are lowered straight from the JSON tokenizer into
     76 the pool — no intermediate `json.Value` tree — which preserves declaration
     77 order and keeps output deterministic.
     78 
     79 ## Testing
     80 
     81 ```sh
     82 make test   # unit + golden tests (odin test src/pkg/jschema)
     83 make e2e    # generate -> compile -> parse sample.json per testdata case
     84 make bench  # performance report (time, throughput, allocations, leaks)
     85 make check  # test + e2e
     86 ```
     87 
     88 Each directory under `testdata/cases/` holds a `schema.json`, the expected
     89 generated code (`expected.odin`), a `sample.json` instance, and a
     90 `check.odin` test that asserts the parsed values. The `openapi` case runs the
     91 full OpenAPI 3.2 document schema end-to-end.