Skip to content

Commit d5a0c4b

Browse files
committed
api: add buildAndCompile helpers
1 parent d9304e6 commit d5a0c4b

8 files changed

Lines changed: 122 additions & 50 deletions

File tree

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,13 @@ let addressSchema =
3939
|> field "city" _.City
4040
|> build
4141
42-
let personSchema =
42+
let codec =
4343
define<Person>
4444
|> construct makePerson
4545
|> field "id" _.Id
4646
|> field "name" _.Name
4747
|> fieldWith "home" _.Home addressSchema
48-
|> build
49-
50-
let codec = Json.compile personSchema
48+
|> Json.buildAndCompile
5149
let person =
5250
{
5351
Id = 42
@@ -76,13 +74,13 @@ That schema reads almost like the data constructor:
7674

7775
The result is not hidden serializer behavior. It is the contract itself, written in normal F#.
7876

79-
If you want the compile step to read a bit smaller in samples, the format modules also expose `Json.codec`, `Xml.codec`, `Yaml.codec`, and `KeyValue.codec` as direct aliases for `compile`.
77+
If the schema only exists inline at the end of the authoring pipeline, `Json.buildAndCompile`, `Xml.buildAndCompile`, `Yaml.buildAndCompile`, and `KeyValue.buildAndCompile` make that terminal step easier to scan. Keep `Json.compile personSchema` when the named schema is reused.
8078

8179
## Why use it
8280

8381
- The schema mirrors the data, so changes to the wire contract are visible in one place.
8482
- Encode and decode come from the same definition, so drift is harder to introduce accidentally.
85-
- `Json.compile` / `Json.codec` and `Xml.compile` / `Xml.codec` reuse the same schema instead of making you maintain separate mappings.
83+
- `Json.compile` and `Xml.compile` reuse the same schema instead of making you maintain separate mappings.
8684
- Domain refinement stays explicit through `Schema.map` and `Schema.tryMap` instead of being buried in serializer settings.
8785
- Versioned message and config contracts stay deliberate because the wire shape is authored directly.
8886

docs/GETTING_STARTED.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,26 @@ define<'T>
3131
|> build
3232
```
3333

34-
Compile explicitly and reuse the resulting codec:
34+
When the schema is named and reused, compile explicitly and keep the resulting codec:
3535

3636
```fsharp
3737
let codec = Json.compile personSchema
3838
let json = Json.serialize codec person
3939
let roundTripped = Json.deserialize codec json
4040
```
4141

42-
The format modules also expose `Json.codec`, `Xml.codec`, `Yaml.codec`, and `KeyValue.codec` as direct aliases for the corresponding `compile` functions. This tutorial uses the shorter `*.codec` form in the small examples to reduce noise, but the separate `compile` step is still the important performance habit: compile once, keep the codec, and reuse it.
42+
When the schema only exists to feed one inline pipeline, end the authoring flow with the format-specific `buildAndCompile` helper:
43+
44+
```fsharp
45+
let codec =
46+
define<Person>
47+
|> construct makePerson
48+
|> field "id" _.Id
49+
|> field "name" _.Name
50+
|> Json.buildAndCompile
51+
```
52+
53+
Use `buildAndCompile` to make inline examples easier to scan. Use `Json.compile personSchema` when the schema itself is named, linked from other schemas, or reused across multiple codecs.
4354

4455
## How to read a schema
4556

@@ -61,14 +72,12 @@ open CodecMapper.Schema
6172
type Person = { Id: int; Name: string }
6273
let makePerson id name = { Id = id; Name = name }
6374
64-
let personSchema =
75+
let jsonCodec =
6576
define<Person>
6677
|> construct makePerson
6778
|> field "id" _.Id
6879
|> field "name" _.Name
69-
|> build
70-
71-
let jsonCodec = Json.codec personSchema
80+
|> Json.buildAndCompile
7281
7382
let person = { Id = 1; Name = "Ada" }
7483
let json = Json.serialize jsonCodec person
@@ -86,7 +95,7 @@ Output:
8695
Name = "Ada" }
8796
```
8897

89-
This tutorial keeps using the shorter `Json.codec` spelling in the small snippets:
98+
This tutorial uses `Json.buildAndCompile` for the small inline snippets:
9099

91100
```fsharp
92101
open CodecMapper
@@ -95,17 +104,15 @@ open CodecMapper.Schema
95104
type Person = { Id: int; Name: string }
96105
let makePerson id name = { Id = id; Name = name }
97106
98-
let personSchema =
107+
let jsonCodec =
99108
define<Person>
100109
|> construct makePerson
101110
|> field "id" _.Id
102111
|> field "name" _.Name
103-
|> build
104-
105-
let jsonCodec = Json.codec personSchema
112+
|> Json.buildAndCompile
106113
```
107114

108-
In longer-lived application code, prefer keeping the explicit `Json.compile personSchema` shape when you want to emphasize that the codec should be created once and reused.
115+
When you keep the schema in a named value, prefer the explicit `Json.compile personSchema` shape so the reuse boundary stays obvious.
109116

110117
## Starting from C#
111118

docs/HOW_TO_MODEL_A_BASIC_RECORD.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ open CodecMapper.Schema
99
type Person = { Id: int; Name: string }
1010
let makePerson id name = { Id = id; Name = name }
1111
12-
let personSchema =
12+
let codec =
1313
define<Person>
1414
|> construct makePerson
1515
|> field "id" _.Id
1616
|> field "name" _.Name
17-
|> build
18-
19-
let codec = Json.codec personSchema
17+
|> Json.buildAndCompile
2018
```
2119

22-
This is the smallest authored schema shape: define the record target, provide the constructor, then map each field explicitly.
20+
This is the smallest authored schema shape: define the record target, provide the constructor, then map each field explicitly and finish with `Json.buildAndCompile`.

src/CodecMapper/Json.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,8 +1378,14 @@ module Json =
13781378
}
13791379

13801380
///
1381-
/// `codec` is the shorter authoring alias for `compile`, so examples can
1382-
/// stay explicit about the compile step without repeating the longer name.
1381+
/// Inline schema pipelines read more clearly when the final `build` and
1382+
/// JSON compile step collapse into one terminal pipeline stage.
1383+
let inline buildAndCompile (builder: Builder<'T, 'T>) : Codec<'T> =
1384+
builder |> Schema.build |> compile
1385+
1386+
///
1387+
/// `codec` remains as the shorter schema-to-codec alias for callers that
1388+
/// prefer the direct `compile schema` shape without the longer name.
13831389
let codec (schema: Schema<'T>) : Codec<'T> = compile schema
13841390

13851391
/// Serializes a value to JSON using a previously compiled codec.

src/CodecMapper/KeyValue.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,14 @@ module KeyValue =
304304
let compile (schema: Schema<'T>) : Codec<'T> = compileUsing Options.defaults schema
305305

306306
///
307-
/// `codec` keeps the default-key-path compile step explicit while giving
308-
/// config examples the same shorter shape as the JSON and XML helpers.
307+
/// Inline schema pipelines read more clearly when the final `build` and
308+
/// key/value compile step collapse into one terminal pipeline stage.
309+
let inline buildAndCompile (builder: Builder<'T, 'T>) : Codec<'T> =
310+
builder |> Schema.build |> compile
311+
312+
///
313+
/// `codec` remains as the shorter schema-to-codec alias for callers that
314+
/// prefer the direct compile step under the default options.
309315
let codec (schema: Schema<'T>) : Codec<'T> = compile schema
310316

311317
/// Serializes a value to a flat key/value map using a previously compiled codec.

src/CodecMapper/Xml.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,14 @@ module Xml =
719719
}
720720

721721
///
722-
/// `codec` mirrors `Json.codec` so multi-format examples can keep the same
723-
/// explicit compile step while using a shorter, copy-paste-friendly name.
722+
/// Inline schema pipelines read more clearly when the final `build` and
723+
/// XML compile step collapse into one terminal pipeline stage.
724+
let inline buildAndCompile (builder: Builder<'T, 'T>) : Codec<'T> =
725+
builder |> Schema.build |> compile
726+
727+
///
728+
/// `codec` mirrors `Json.codec` for callers that still prefer the direct
729+
/// schema-to-codec alias over the longer `compile` name.
724730
let codec (schema: Schema<'T>) : Codec<'T> = compile schema
725731

726732
/// Serializes a value to XML using the schema-derived root element name.

src/CodecMapper/Yaml.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,14 @@ module Yaml =
380380
}
381381

382382
///
383-
/// `codec` mirrors the other format modules so config-oriented examples
384-
/// can keep the compile step explicit without extra visual noise.
383+
/// Inline schema pipelines read more clearly when the final `build` and
384+
/// YAML compile step collapse into one terminal pipeline stage.
385+
let inline buildAndCompile (builder: Builder<'T, 'T>) : Codec<'T> =
386+
builder |> Schema.build |> compile
387+
388+
///
389+
/// `codec` mirrors the other format modules for callers that still prefer
390+
/// the shorter schema-to-codec alias over the longer `compile` name.
385391
let codec (schema: Schema<'T>) : Codec<'T> = compile schema
386392

387393
/// Serializes a value to YAML using a previously compiled codec.

tests/CodecMapper.Tests/SchemaDslTests.fs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,17 @@ let ``Round-trip list of strings JSON`` () =
8989
test <@ decoded = value @>
9090

9191
[<Fact>]
92-
let ``Codec aliases mirror compile helpers across formats`` () =
93-
let personSchema =
92+
let ``buildAndCompile composes build and compile across formats`` () =
93+
let person = {
94+
Id = 7
95+
Name = "Alias"
96+
Home = {
97+
Street = "Codec Street"
98+
City = "Adelaide"
99+
}
100+
}
101+
102+
let jsonCodec =
94103
Schema.define<Person>
95104
|> Schema.construct makePerson
96105
|> Schema.field "id" _.Id
@@ -103,26 +112,62 @@ let ``Codec aliases mirror compile helpers across formats`` () =
103112
|> Schema.field "street" _.Street
104113
|> Schema.field "city" _.City
105114
|> Schema.build)
106-
|> Schema.build
115+
|> Json.buildAndCompile
107116

108-
let person = {
109-
Id = 7
110-
Name = "Alias"
111-
Home = {
112-
Street = "Codec Street"
113-
City = "Adelaide"
114-
}
115-
}
117+
let xmlCodec =
118+
Schema.define<Person>
119+
|> Schema.construct makePerson
120+
|> Schema.field "id" _.Id
121+
|> Schema.field "name" _.Name
122+
|> Schema.fieldWith
123+
"home"
124+
_.Home
125+
(Schema.define<Address>
126+
|> Schema.construct makeAddress
127+
|> Schema.field "street" _.Street
128+
|> Schema.field "city" _.City
129+
|> Schema.build)
130+
|> Xml.buildAndCompile
116131

117-
let json = Json.serialize (Json.codec personSchema) person
118-
let xml = Xml.serialize (Xml.codec personSchema) person
119-
let yaml = Yaml.serialize (Yaml.codec personSchema) person
120-
let keyValue = KeyValue.serialize (KeyValue.codec personSchema) person
132+
let yamlCodec =
133+
Schema.define<Person>
134+
|> Schema.construct makePerson
135+
|> Schema.field "id" _.Id
136+
|> Schema.field "name" _.Name
137+
|> Schema.fieldWith
138+
"home"
139+
_.Home
140+
(Schema.define<Address>
141+
|> Schema.construct makeAddress
142+
|> Schema.field "street" _.Street
143+
|> Schema.field "city" _.City
144+
|> Schema.build)
145+
|> Yaml.buildAndCompile
146+
147+
let keyValueCodec =
148+
Schema.define<Person>
149+
|> Schema.construct makePerson
150+
|> Schema.field "id" _.Id
151+
|> Schema.field "name" _.Name
152+
|> Schema.fieldWith
153+
"home"
154+
_.Home
155+
(Schema.define<Address>
156+
|> Schema.construct makeAddress
157+
|> Schema.field "street" _.Street
158+
|> Schema.field "city" _.City
159+
|> Schema.build)
160+
|> KeyValue.buildAndCompile
161+
162+
let json = Json.serialize jsonCodec person
163+
let xml = Xml.serialize xmlCodec person
164+
let yaml = Yaml.serialize yamlCodec person
165+
let keyValue = KeyValue.serialize keyValueCodec person
121166

122-
test <@ Json.deserialize (Json.codec personSchema) json = person @>
123-
test <@ Xml.deserialize (Xml.codec personSchema) xml = person @>
124-
test <@ Yaml.deserialize (Yaml.codec personSchema) yaml = person @>
125-
test <@ KeyValue.deserialize (KeyValue.codec personSchema) keyValue = person @>
167+
test <@ Json.deserialize jsonCodec json = person @>
168+
test <@ Xml.deserialize xmlCodec xml = person @>
169+
test <@ Yaml.deserialize yamlCodec yaml = person @>
170+
test <@ KeyValue.deserialize keyValueCodec keyValue = person @>
126171

127172
[<Fact>]
128173
let ``Round-trip mapped type (PersonId) JSON`` () =

0 commit comments

Comments
 (0)