@zio.dev/zio-blocks 0.0.33 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/guides/compile-time-resource-safety-with-scope.md +16 -17
- package/guides/getting-started-with-mux.md +1507 -0
- package/guides/query-dsl-extending.md +161 -102
- package/guides/query-dsl-fluent-builder.md +217 -157
- package/guides/query-dsl-reified-optics.md +12 -10
- package/guides/query-dsl-sql.md +246 -165
- package/guides/telemetry-guide.md +1069 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +292 -50
- package/package.json +1 -1
- package/plans/config-follow-up-prs.md +188 -0
- package/plans/config-pr-assessment-roadmap.md +310 -0
- package/reference/MuxDataFlow.jsx +250 -0
- package/reference/async.md +651 -0
- package/reference/chunk.md +3533 -308
- package/reference/codegen/case-class.md +436 -0
- package/reference/codegen/emitter-config.md +383 -0
- package/reference/codegen/examples.md +664 -0
- package/reference/codegen/field.md +316 -0
- package/reference/codegen/index.md +317 -0
- package/reference/codegen/scala-emitter.md +392 -0
- package/reference/codegen/scala-file.md +276 -0
- package/reference/codegen/sealed-trait.md +408 -0
- package/reference/codegen/type-definition.md +340 -0
- package/reference/codegen/type-ref.md +201 -0
- package/reference/combinators.md +347 -117
- package/reference/config.md +158 -0
- package/reference/context.md +4 -4
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +825 -0
- package/reference/endpoint/path-codec.md +237 -0
- package/reference/endpoint/route-pattern.md +196 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +212 -0
- package/reference/html.md +1120 -0
- package/reference/htmx/attribute-values.md +359 -0
- package/reference/htmx/hx-encoding.md +111 -0
- package/reference/htmx/hx-params.md +204 -0
- package/reference/htmx/hx-swap.md +276 -0
- package/reference/htmx/hx-sync.md +251 -0
- package/reference/htmx/hx-target.md +314 -0
- package/reference/htmx/hx-trigger.md +457 -0
- package/reference/htmx/hx-url-update.md +239 -0
- package/reference/htmx/index.md +855 -0
- package/reference/http-model/index.md +47 -0
- package/reference/http-model/model.md +1481 -0
- package/reference/http-model/schema.md +747 -0
- package/reference/maybe.md +826 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +823 -0
- package/reference/openapi.md +1351 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -2
- package/reference/resource-management/scope.md +28 -12
- package/reference/resource-management/wire.md +3 -7
- package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
- package/reference/ringbuffer/MpscDiagram.jsx +618 -0
- package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
- package/reference/ringbuffer/SpscDiagram.jsx +677 -0
- package/reference/ringbuffer/advanced.mdx +109 -0
- package/reference/ringbuffer/index.mdx +145 -0
- package/reference/ringbuffer/mpmc.mdx +151 -0
- package/reference/ringbuffer/mpsc.mdx +132 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +344 -0
- package/reference/{allows.md → schema/allows.md} +4 -4
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +2 -3
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +480 -0
- package/reference/schema/built-in-codecs/csv.md +564 -0
- package/reference/schema/built-in-codecs/index.md +77 -0
- package/reference/schema/built-in-codecs/json/index.md +295 -0
- package/reference/schema/built-in-codecs/json/json-config.md +217 -0
- package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
- package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
- package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
- package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
- package/reference/schema/built-in-codecs/messagepack.md +508 -0
- package/reference/schema/built-in-codecs/thrift.md +433 -0
- package/reference/schema/built-in-codecs/toon.md +1078 -0
- package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
- package/reference/schema/built-in-codecs/yaml.md +552 -0
- package/reference/{codec.md → schema/codec.md} +10 -10
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +50 -0
- package/reference/schema/migration.md +297 -0
- package/reference/{modifier.md → schema/modifier.md} +58 -7
- package/reference/{optics.md → schema/optics.md} +2 -2
- package/reference/{patch.md → schema/patch.md} +1 -1
- package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
- package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
- package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
- package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
- package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
- package/reference/{schema.md → schema/schema.md} +12 -0
- package/reference/{structural-types.md → schema/structural-types.md} +1 -1
- package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
- package/reference/smithy.md +533 -0
- package/reference/sql/db-codec-deriver.md +71 -0
- package/reference/sql/db-codec.md +687 -0
- package/reference/sql/db-con.md +271 -0
- package/reference/sql/db-connection.md +153 -0
- package/reference/sql/db-param-writer.md +77 -0
- package/reference/sql/db-param.md +66 -0
- package/reference/sql/db-result-reader.md +146 -0
- package/reference/sql/db-tx.md +82 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +254 -0
- package/reference/sql/index.md +341 -0
- package/reference/sql/repo.md +600 -0
- package/reference/sql/sql-dialect.md +73 -0
- package/reference/sql/sql-logger.md +62 -0
- package/reference/sql/sql-name-mapper.md +70 -0
- package/reference/sql/table-metadata.md +134 -0
- package/reference/sql/table.md +448 -0
- package/reference/sql/transactor-zio.md +399 -0
- package/reference/sql/transactor.md +353 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/concurrent-operators.md +106 -0
- package/reference/streams/index.md +653 -0
- package/reference/streams/pipeline.md +718 -0
- package/reference/streams/reader.md +1284 -0
- package/reference/streams/scala-2-compatibility.md +55 -0
- package/reference/streams/sink.md +1426 -0
- package/reference/streams/stream.md +2526 -0
- package/reference/streams/writer.md +1045 -0
- package/reference/streams/zero-boxing.md +275 -0
- package/reference/telemetry.md +693 -0
- package/reference/typeid.md +5 -19
- package/sidebars.js +238 -43
- package/reference/formats.md +0 -694
- package/reference/http-model.md +0 -1716
- package/reference/streams.md +0 -989
- package/ringbuffer.md +0 -249
- /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
- /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
- /package/reference/{lazy.md → schema/lazy.md} +0 -0
- /package/reference/{reflect.md → schema/reflect.md} +0 -0
- /package/reference/{registers.md → schema/registers.md} +0 -0
- /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
- /package/reference/{syntax.md → schema/syntax.md} +0 -0
- /package/reference/{validation.md → schema/validation.md} +0 -0
|
@@ -167,6 +167,68 @@ import zio.blocks.schema.json._
|
|
|
167
167
|
val jsonCodec = Person.schema.derive(JsonFormat)
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
+
## Customizing Derivation with Instance and Modifier Overrides
|
|
171
|
+
|
|
172
|
+
By default, `Deriver` automatically derives codecs for all types. But sometimes you need to customize how specific types are encoded or decoded—for example, encoding `LocalDate` as `"dd/MM/yyyy"` instead of ISO format.
|
|
173
|
+
|
|
174
|
+
ZIO Blocks provides three levels of customization, in progressive order:
|
|
175
|
+
|
|
176
|
+
1. **Type-level override** (`Deriver.withInstance`) — Override the codec for ALL occurrences of a type. Configure once, use everywhere:
|
|
177
|
+
|
|
178
|
+
```scala
|
|
179
|
+
import java.time.LocalDate
|
|
180
|
+
import java.time.format.DateTimeFormatter
|
|
181
|
+
|
|
182
|
+
// Create a custom JsonCodec for LocalDate
|
|
183
|
+
val customDateCodec: JsonCodec[LocalDate] = new JsonCodec[LocalDate] {
|
|
184
|
+
private val fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy")
|
|
185
|
+
def decodeValue(in: JsonReader): LocalDate = LocalDate.parse(in.readString(), fmt)
|
|
186
|
+
def encodeValue(x: LocalDate, out: JsonWriter): Unit = out.writeVal(fmt.format(x))
|
|
187
|
+
// ... AST overrides ...
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Configure the deriver once
|
|
191
|
+
val myDeriver = JsonCodecDeriver.withInstance[LocalDate](customDateCodec)
|
|
192
|
+
|
|
193
|
+
// Use everywhere — all LocalDate fields use the custom codec
|
|
194
|
+
val codec1 = Schema[Event].deriving(myDeriver).derive
|
|
195
|
+
val codec2 = Schema[Meeting].deriving(myDeriver).derive
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
2. **Field-level override** (`Deriver.withInstance` with typeId + termName) — Override a specific field only:
|
|
199
|
+
|
|
200
|
+
```scala
|
|
201
|
+
// Only Event.date uses custom format; other LocalDate fields are unchanged
|
|
202
|
+
val deriver = JsonCodecDeriver.withInstance[Event, LocalDate](
|
|
203
|
+
TypeId.of[Event], "date", customDateCodec
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
3. **Modifier override** (`Deriver.withModifier`) — Rename fields, add aliases:
|
|
208
|
+
|
|
209
|
+
```scala
|
|
210
|
+
val deriver = JsonCodecDeriver.withModifier(
|
|
211
|
+
TypeId.of[Person], "firstName", Modifier.rename("first_name")
|
|
212
|
+
)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Chaining Overrides
|
|
216
|
+
|
|
217
|
+
Overrides compose, allowing you to build complex derivers incrementally:
|
|
218
|
+
|
|
219
|
+
```scala
|
|
220
|
+
val myDeriver = JsonCodecDeriver
|
|
221
|
+
.withInstance[LocalDate](customDateCodec)
|
|
222
|
+
.withModifier(TypeId.of[Event], "name", Modifier.rename("title"))
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Important Notes
|
|
226
|
+
|
|
227
|
+
- `withInstance` and `withModifier` return a NEW deriver (they are immutable).
|
|
228
|
+
- When combined with `DerivationBuilder`, deriver-level instance overrides take precedence over builder-level instance overrides. Modifier override precedence is order-sensitive and should not be assumed to follow the same rule.
|
|
229
|
+
- Unknown `termName` values are silently ignored.
|
|
230
|
+
- The `B` type parameter in field-level `withInstance` is not statically checked against the actual field type.
|
|
231
|
+
|
|
170
232
|
## Example 1: Deriving a `Show` Type Class Instance
|
|
171
233
|
|
|
172
234
|
Let's say we want to derive a `Show` type class instance for any type of type `A`:
|
|
@@ -1455,7 +1517,7 @@ Now we can use the derived `Gen[Person]` instance to generate random `Person` va
|
|
|
1455
1517
|
|
|
1456
1518
|
```scala
|
|
1457
1519
|
val random = new Random(42) // Seeded for reproducible output
|
|
1458
|
-
// random: Random = scala.util.Random@
|
|
1520
|
+
// random: Random = scala.util.Random@d903a4c
|
|
1459
1521
|
|
|
1460
1522
|
Person.gen.generate(random)
|
|
1461
1523
|
// res14: Person = Person(name = "p", age = -1360544799)
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: smithy
|
|
3
|
+
title: "Smithy"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`zio-blocks-smithy` is a **Smithy IDL parser and AST library** providing a complete representation of Smithy 2.0 API models. It enables parsing Smithy IDL text into rich data structures, querying shape definitions, and pretty-printing models back to valid IDL syntax—all without external dependencies.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add the library to your build configuration:
|
|
11
|
+
|
|
12
|
+
```scala
|
|
13
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-smithy" % "0.0.51"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Supported Scala versions: 2.13.x and 3.x
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
Parse Smithy IDL text into a model, query shapes, and serialize back:
|
|
21
|
+
|
|
22
|
+
```scala
|
|
23
|
+
import zio.blocks.smithy._
|
|
24
|
+
|
|
25
|
+
val smithyText = """$version: "2"
|
|
26
|
+
namespace com.example.api
|
|
27
|
+
|
|
28
|
+
structure User {
|
|
29
|
+
@required
|
|
30
|
+
id: String
|
|
31
|
+
name: String
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
operation GetUser {
|
|
35
|
+
input: GetUserInput
|
|
36
|
+
output: User
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
structure GetUserInput {
|
|
40
|
+
@required
|
|
41
|
+
id: String
|
|
42
|
+
}
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
// Parse IDL text into a model
|
|
46
|
+
val result = SmithyModel.parse(smithyText)
|
|
47
|
+
|
|
48
|
+
// Access shapes and data
|
|
49
|
+
result match {
|
|
50
|
+
case Right(model) =>
|
|
51
|
+
model.findShape("User").foreach { userDef =>
|
|
52
|
+
println(s"Found shape: ${userDef.name}")
|
|
53
|
+
}
|
|
54
|
+
case Left(error) =>
|
|
55
|
+
println(s"Parse error: ${error.message}")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Serialize back to IDL
|
|
59
|
+
result.foreach { model =>
|
|
60
|
+
val idlText = model.prettyPrint
|
|
61
|
+
println(idlText)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Core Types
|
|
66
|
+
|
|
67
|
+
The library provides core types that work together to parse, query, and serialize Smithy models. The main types work together in a parsing → querying → serialization pipeline:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Smithy IDL Text
|
|
71
|
+
↓
|
|
72
|
+
SmithyModel.parse (public API)
|
|
73
|
+
↓
|
|
74
|
+
SmithyModel (contains shapes, metadata, traits)
|
|
75
|
+
├─ shapes: List[ShapeDefinition]
|
|
76
|
+
│ └─ shape: Shape (sealed trait — central type)
|
|
77
|
+
│ ├─ StructureShape(members: List[MemberDefinition])
|
|
78
|
+
│ ├─ ListShape(member: MemberDefinition)
|
|
79
|
+
│ ├─ MapShape(key: MemberDefinition, value: MemberDefinition)
|
|
80
|
+
│ ├─ ServiceShape(operations, resources, errors)
|
|
81
|
+
│ ├─ OperationShape(input, output, errors)
|
|
82
|
+
│ ├─ UnionShape(members: List[MemberDefinition])
|
|
83
|
+
│ ├─ StringShape, BooleanShape, IntegerShape, etc.
|
|
84
|
+
│ └─ ... (and other shape subtypes)
|
|
85
|
+
├─ MemberDefinition(name: String, target: ShapeId, traits: List[TraitApplication])
|
|
86
|
+
├─ TraitApplication(id: ShapeId, value: Option[NodeValue])
|
|
87
|
+
├─ ShapeId (namespace + name identifier)
|
|
88
|
+
└─ NodeValue (metadata values: String, Number, Boolean, Array, Object, Null)
|
|
89
|
+
↓
|
|
90
|
+
SmithyModel.prettyPrint (public API)
|
|
91
|
+
↓
|
|
92
|
+
Smithy IDL Text
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The root container for a Smithy model. Contains version, namespace, shapes, metadata, and trait applications. The case class and companion object expose the following API:
|
|
96
|
+
|
|
97
|
+
```scala
|
|
98
|
+
case class SmithyModel(
|
|
99
|
+
version: String, // Smithy version (e.g., "2")
|
|
100
|
+
namespace: String,
|
|
101
|
+
useStatements: List[ShapeId],
|
|
102
|
+
metadata: Map[String, NodeValue],
|
|
103
|
+
shapes: List[ShapeDefinition],
|
|
104
|
+
applyStatements: List[ApplyStatement] = Nil
|
|
105
|
+
) {
|
|
106
|
+
def findShape(name: String): Option[ShapeDefinition]
|
|
107
|
+
def allShapeIds: List[ShapeId]
|
|
108
|
+
def prettyPrint: String
|
|
109
|
+
def prettyPrint(indent: Int): String
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
object SmithyModel {
|
|
113
|
+
def parse(input: String): Either[SmithyError, SmithyModel]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Parsing
|
|
118
|
+
|
|
119
|
+
Parse Smithy IDL text into structured models using `SmithyModel.parse`, handle errors, and validate round-trips.
|
|
120
|
+
|
|
121
|
+
### Basic Parsing
|
|
122
|
+
|
|
123
|
+
Parse Smithy IDL text and handle the result:
|
|
124
|
+
|
|
125
|
+
```scala
|
|
126
|
+
import zio.blocks.smithy._
|
|
127
|
+
|
|
128
|
+
val smithyText = """$version: "2"
|
|
129
|
+
namespace com.example
|
|
130
|
+
|
|
131
|
+
string Name
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
SmithyModel.parse(smithyText) match {
|
|
135
|
+
case Right(model) =>
|
|
136
|
+
println(s"Parsed ${model.shapes.length} shapes")
|
|
137
|
+
case Left(error) =>
|
|
138
|
+
println(s"Error: ${error.message}")
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Handling Parse Errors
|
|
143
|
+
|
|
144
|
+
Access error details including line and column information when parsing fails. `SmithyError` provides detailed context to help locate and fix issues in your Smithy definitions:
|
|
145
|
+
|
|
146
|
+
```scala
|
|
147
|
+
import zio.blocks.smithy._
|
|
148
|
+
|
|
149
|
+
val invalidSmithy = """$version: "2"
|
|
150
|
+
namespace com.example
|
|
151
|
+
|
|
152
|
+
structure User {
|
|
153
|
+
invalid syntax
|
|
154
|
+
}
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
SmithyModel.parse(invalidSmithy) match {
|
|
158
|
+
case Right(_) =>
|
|
159
|
+
println("Unexpected success")
|
|
160
|
+
case Left(error) =>
|
|
161
|
+
println(s"Error at line ${error.line}, column ${error.column}: ${error.message}")
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Round-Trip Validation
|
|
166
|
+
|
|
167
|
+
Verify a model parses correctly by round-tripping (parse → serialize → parse again):
|
|
168
|
+
|
|
169
|
+
```scala
|
|
170
|
+
import zio.blocks.smithy._
|
|
171
|
+
|
|
172
|
+
val original = """$version: "2"
|
|
173
|
+
namespace com.example
|
|
174
|
+
|
|
175
|
+
string MyString
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
val parsed = SmithyModel.parse(original)
|
|
179
|
+
val reprinted = parsed.map(_.prettyPrint)
|
|
180
|
+
val reparsed = reprinted.flatMap(SmithyModel.parse)
|
|
181
|
+
|
|
182
|
+
println(reparsed.isRight) // true if round-trip succeeds
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Querying & Traversing Shapes
|
|
186
|
+
|
|
187
|
+
Once you have a parsed model, query shapes by name, pattern match on shape types, and traverse their members.
|
|
188
|
+
|
|
189
|
+
### Finding Shapes
|
|
190
|
+
|
|
191
|
+
Locate shapes by name or retrieve all shape identifiers:
|
|
192
|
+
|
|
193
|
+
```scala
|
|
194
|
+
import zio.blocks.smithy._
|
|
195
|
+
|
|
196
|
+
val model = SmithyModel.parse("""$version: "2"
|
|
197
|
+
namespace example
|
|
198
|
+
|
|
199
|
+
structure User {
|
|
200
|
+
id: String
|
|
201
|
+
name: String
|
|
202
|
+
}
|
|
203
|
+
""").toOption.get
|
|
204
|
+
|
|
205
|
+
// Find by name
|
|
206
|
+
model.findShape("User").foreach { shapeDef =>
|
|
207
|
+
println(s"Found: ${shapeDef.name}")
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Get all shape IDs
|
|
211
|
+
val allIds = model.allShapeIds
|
|
212
|
+
println(s"Total shapes: ${allIds.length}")
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Pattern Matching on Shapes
|
|
216
|
+
|
|
217
|
+
Determine shape type and access type-specific properties:
|
|
218
|
+
|
|
219
|
+
```scala
|
|
220
|
+
import zio.blocks.smithy._
|
|
221
|
+
|
|
222
|
+
val model = SmithyModel.parse("""$version: "2"
|
|
223
|
+
namespace example
|
|
224
|
+
|
|
225
|
+
structure User { id: String }
|
|
226
|
+
list UserIds { member: String }
|
|
227
|
+
""").toOption.get
|
|
228
|
+
|
|
229
|
+
model.findShape("User").foreach { shapeDef =>
|
|
230
|
+
shapeDef.shape match {
|
|
231
|
+
case struct: StructureShape =>
|
|
232
|
+
println(s"Structure with ${struct.members.length} members")
|
|
233
|
+
case list: ListShape =>
|
|
234
|
+
println(s"List of ${list.member.target}")
|
|
235
|
+
case _ =>
|
|
236
|
+
println("Other shape type")
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Traversing Members
|
|
242
|
+
|
|
243
|
+
Iterate over structure/union members and inspect their traits:
|
|
244
|
+
|
|
245
|
+
```scala
|
|
246
|
+
import zio.blocks.smithy._
|
|
247
|
+
|
|
248
|
+
val model = SmithyModel.parse("""$version: "2"
|
|
249
|
+
namespace example
|
|
250
|
+
|
|
251
|
+
structure User {
|
|
252
|
+
@required
|
|
253
|
+
id: String
|
|
254
|
+
name: String
|
|
255
|
+
}
|
|
256
|
+
""").toOption.get
|
|
257
|
+
|
|
258
|
+
model.findShape("User").foreach { shapeDef =>
|
|
259
|
+
shapeDef.shape match {
|
|
260
|
+
case struct: StructureShape =>
|
|
261
|
+
struct.members.foreach { member =>
|
|
262
|
+
val required = member.traits.exists(_.id.name == "required")
|
|
263
|
+
println(s"${member.name}: ${member.target} (required: $required)")
|
|
264
|
+
}
|
|
265
|
+
case _ => ()
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Building Models Programmatically
|
|
271
|
+
|
|
272
|
+
Construct Smithy models in code by creating shapes, adding traits, and assembling them into a complete model.
|
|
273
|
+
|
|
274
|
+
### Creating Shapes
|
|
275
|
+
|
|
276
|
+
Programmatically construct shapes and assemble them into a complete model:
|
|
277
|
+
|
|
278
|
+
```scala
|
|
279
|
+
import zio.blocks.smithy._
|
|
280
|
+
|
|
281
|
+
val userStructure = StructureShape(
|
|
282
|
+
"User",
|
|
283
|
+
traits = Nil,
|
|
284
|
+
members = List(
|
|
285
|
+
MemberDefinition(
|
|
286
|
+
"id",
|
|
287
|
+
ShapeId("smithy.api", "String"),
|
|
288
|
+
traits = List(TraitApplication.required)
|
|
289
|
+
),
|
|
290
|
+
MemberDefinition(
|
|
291
|
+
"name",
|
|
292
|
+
ShapeId("smithy.api", "String"),
|
|
293
|
+
traits = Nil
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
val model = SmithyModel(
|
|
299
|
+
version = "2",
|
|
300
|
+
namespace = "com.example",
|
|
301
|
+
useStatements = Nil,
|
|
302
|
+
metadata = Map.empty,
|
|
303
|
+
shapes = List(ShapeDefinition("User", userStructure))
|
|
304
|
+
)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Adding Traits
|
|
308
|
+
|
|
309
|
+
Attach metadata traits to shapes during construction. `TraitApplication` provides companion object helper methods like `required`, `documentation`, and others for common traits:
|
|
310
|
+
|
|
311
|
+
```scala
|
|
312
|
+
import zio.blocks.smithy._
|
|
313
|
+
|
|
314
|
+
val serviceShape = ServiceShape(
|
|
315
|
+
"UserService",
|
|
316
|
+
traits = List(
|
|
317
|
+
TraitApplication.documentation("User management API")
|
|
318
|
+
),
|
|
319
|
+
version = Some("1.0"),
|
|
320
|
+
operations = List(
|
|
321
|
+
ShapeId("com.example", "GetUser"),
|
|
322
|
+
ShapeId("com.example", "CreateUser")
|
|
323
|
+
),
|
|
324
|
+
resources = Nil,
|
|
325
|
+
errors = Nil
|
|
326
|
+
)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Serializing Models
|
|
330
|
+
|
|
331
|
+
Convert models back to valid Smithy IDL text using `prettyPrint`, with options for custom formatting.
|
|
332
|
+
|
|
333
|
+
### Basic Serialization
|
|
334
|
+
|
|
335
|
+
Convert a model to valid Smithy IDL text:
|
|
336
|
+
|
|
337
|
+
```scala
|
|
338
|
+
import zio.blocks.smithy._
|
|
339
|
+
|
|
340
|
+
val model = SmithyModel(
|
|
341
|
+
version = "2",
|
|
342
|
+
namespace = "com.example",
|
|
343
|
+
useStatements = Nil,
|
|
344
|
+
metadata = Map.empty,
|
|
345
|
+
shapes = List(
|
|
346
|
+
ShapeDefinition("Name", StringShape("Name"))
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
val idlText = model.prettyPrint
|
|
351
|
+
println(idlText)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Custom Indentation
|
|
355
|
+
|
|
356
|
+
Control indentation width when serializing models:
|
|
357
|
+
|
|
358
|
+
```scala
|
|
359
|
+
import zio.blocks.smithy._
|
|
360
|
+
|
|
361
|
+
val model = SmithyModel(
|
|
362
|
+
version = "2",
|
|
363
|
+
namespace = "com.example",
|
|
364
|
+
useStatements = Nil,
|
|
365
|
+
metadata = Map.empty,
|
|
366
|
+
shapes = List(
|
|
367
|
+
ShapeDefinition("Data", StructureShape(
|
|
368
|
+
"Data",
|
|
369
|
+
traits = Nil,
|
|
370
|
+
members = List(
|
|
371
|
+
MemberDefinition("field1", ShapeId("smithy.api", "String")),
|
|
372
|
+
MemberDefinition("field2", ShapeId("smithy.api", "String"))
|
|
373
|
+
)
|
|
374
|
+
))
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
val compact = model.prettyPrint(indent = 2)
|
|
379
|
+
val verbose = model.prettyPrint(indent = 8)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Common Use-Cases
|
|
383
|
+
|
|
384
|
+
See how to apply Smithy parsing and querying to real-world workflows: code generation, validation, and model transformation.
|
|
385
|
+
|
|
386
|
+
### Use-Case 1: Code Generation
|
|
387
|
+
|
|
388
|
+
Load a Smithy model and generate code for each operation:
|
|
389
|
+
|
|
390
|
+
```scala
|
|
391
|
+
import zio.blocks.smithy._
|
|
392
|
+
|
|
393
|
+
val model = SmithyModel.parse("""$version: "2"
|
|
394
|
+
namespace api
|
|
395
|
+
|
|
396
|
+
service MyService {
|
|
397
|
+
operations: [GetUser, CreateUser]
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
@http(method: "GET", uri: "/users/{id}")
|
|
401
|
+
operation GetUser {
|
|
402
|
+
input: GetUserInput
|
|
403
|
+
output: User
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@http(method: "POST", uri: "/users")
|
|
407
|
+
operation CreateUser {
|
|
408
|
+
input: CreateUserInput
|
|
409
|
+
output: User
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
structure User { id: String, name: String }
|
|
413
|
+
structure GetUserInput { @required id: String }
|
|
414
|
+
structure CreateUserInput { @required name: String }
|
|
415
|
+
""").toOption.get
|
|
416
|
+
|
|
417
|
+
// Generate code stubs for each operation by pattern matching:
|
|
418
|
+
|
|
419
|
+
model.shapes.foreach { shapeDef =>
|
|
420
|
+
shapeDef.shape match {
|
|
421
|
+
case op: OperationShape =>
|
|
422
|
+
println(s"// Generate operation: ${op.name}")
|
|
423
|
+
op.input.foreach(in => println(s"// input: ${in.name}"))
|
|
424
|
+
op.output.foreach(out => println(s"// output: ${out.name}"))
|
|
425
|
+
case _ => ()
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Use-Case 2: Validation & Analysis
|
|
431
|
+
|
|
432
|
+
Find deprecated shapes and analyze trait coverage:
|
|
433
|
+
|
|
434
|
+
```scala
|
|
435
|
+
import zio.blocks.smithy._
|
|
436
|
+
|
|
437
|
+
val model = SmithyModel.parse("""$version: "2"
|
|
438
|
+
namespace example
|
|
439
|
+
|
|
440
|
+
@deprecated
|
|
441
|
+
structure LegacyUser { id: String }
|
|
442
|
+
|
|
443
|
+
structure ModernUser {
|
|
444
|
+
@required
|
|
445
|
+
id: String
|
|
446
|
+
email: String
|
|
447
|
+
}
|
|
448
|
+
""").toOption.get
|
|
449
|
+
|
|
450
|
+
// Find all deprecated shapes:
|
|
451
|
+
|
|
452
|
+
val deprecated = model.shapes.filter { shapeDef =>
|
|
453
|
+
shapeDef.shape.traits.exists(_.id.name == "deprecated")
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
println(s"Deprecated shapes: ${deprecated.map(_.name)}")
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Use-Case 3: Model Transformation
|
|
460
|
+
|
|
461
|
+
Parse, modify, and re-serialize a model with updated metadata:
|
|
462
|
+
|
|
463
|
+
```scala
|
|
464
|
+
import zio.blocks.smithy._
|
|
465
|
+
|
|
466
|
+
val original = """$version: "2"
|
|
467
|
+
namespace com.example
|
|
468
|
+
|
|
469
|
+
string UserId
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
val modified = SmithyModel.parse(original).map { model =>
|
|
473
|
+
// Add metadata to the model:
|
|
474
|
+
|
|
475
|
+
val newMetadata = model.metadata + ("version" -> NodeValue.String("1.0"))
|
|
476
|
+
model.copy(metadata = newMetadata)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
modified.foreach { model =>
|
|
480
|
+
println(model.prettyPrint)
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## Running the Examples
|
|
485
|
+
|
|
486
|
+
All code from this guide is available as runnable examples in the `smithy-examples` module. Examples demonstrate different aspects of the Smithy library.
|
|
487
|
+
|
|
488
|
+
**1. Clone the repository and navigate to the project:**
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
git clone https://github.com/zio/zio-blocks.git
|
|
492
|
+
cd zio-blocks
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**2. Run individual examples with sbt:**
|
|
496
|
+
|
|
497
|
+
### Step 1: Basic Parsing and Querying
|
|
498
|
+
|
|
499
|
+
Parse Smithy IDL text, find shapes by name, and access their structure and metadata:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
sbt "smithy-examples/runMain smithyexample.BasicParsingAndQuerying"
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Step 2: Building Models Programmatically
|
|
506
|
+
|
|
507
|
+
Construct Smithy models in code by creating shapes, adding traits, and assembling them into a complete model:
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
sbt "smithy-examples/runMain smithyexample.BuildingModelsAndTraits"
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Step 3: Validation and Analysis
|
|
514
|
+
|
|
515
|
+
Analyze Smithy models for completeness, find deprecated shapes, check for documentation, and validate API contracts:
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
sbt "smithy-examples/runMain smithyexample.ValidationAndAnalysis"
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Step 4: Complete Example — Book Store API
|
|
522
|
+
|
|
523
|
+
A comprehensive end-to-end workflow showing a complete book store API model with parsing, entity analysis, error handling, code generation, and statistics:
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
sbt "smithy-examples/runMain smithyexample.BookStoreAPI"
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
**3. Or compile all examples at once:**
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
sbt "smithy-examples/compile"
|
|
533
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: db-codec-deriver
|
|
3
|
+
title: "DbCodecDeriver"
|
|
4
|
+
description: "Reference for DbCodecDeriver, the schema-driven derivation engine that converts a Schema[A] into a DbCodec[A] in the sql module."
|
|
5
|
+
keywords:
|
|
6
|
+
- "DbCodecDeriver schema derivation"
|
|
7
|
+
- "DbCodec automatic derivation"
|
|
8
|
+
- "Deriver DbCodec"
|
|
9
|
+
- "SqlNameMapper column naming"
|
|
10
|
+
- "withColumnNameMapper"
|
|
11
|
+
- "deriveRecord deriveVariant"
|
|
12
|
+
- "JSONB fallback codec"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
`DbCodecDeriver` is a schema-driven derivation engine that converts a `Schema[A]` into a `DbCodec[A]`.
|
|
16
|
+
|
|
17
|
+
The companion object `DbCodecDeriver` is itself an instance of the class constructed with the default `SqlNameMapper.SnakeCase`. `DbCodec.derived`, `DbCodec.builder`, `DbCodec.derivedWith`, and `Table.derived` all delegate to this deriver; application code rarely constructs or calls it directly.
|
|
18
|
+
|
|
19
|
+
Key properties:
|
|
20
|
+
- **Schema-driven** — derivation is entirely data-driven from the `Schema`'s `Reflect` tree; no separate macro or annotation processor is involved beyond `Schema.derived`.
|
|
21
|
+
- **Configurable naming** — the `columnNameMapper: SqlNameMapper` constructor parameter controls how Scala field names become SQL column names; the default is `SnakeCase`.
|
|
22
|
+
- **Annotation-aware** — `@Modifier.transient` fields are skipped; `@Modifier.rename` overrides the mapper for individual fields; `@Modifier.config("sql.inline","true")` flattens a nested record into the parent's column list.
|
|
23
|
+
- **JSONB fallback** — non-inline nested records, sequences, maps, and dynamic values fall back to a single `TEXT` / `JSONB` column backed by `DbCodec.jsonb`.
|
|
24
|
+
|
|
25
|
+
The structural shape of `DbCodecDeriver` is:
|
|
26
|
+
|
|
27
|
+
```scala
|
|
28
|
+
class DbCodecDeriver(columnNameMapper: SqlNameMapper = SqlNameMapper.SnakeCase)
|
|
29
|
+
extends Deriver[DbCodec]
|
|
30
|
+
|
|
31
|
+
object DbCodecDeriver extends DbCodecDeriver(SqlNameMapper.SnakeCase) {
|
|
32
|
+
def withColumnNameMapper(mapper: SqlNameMapper): DbCodecDeriver
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
The following example shows the three most common ways to reach `DbCodecDeriver`: through the `derives` clause on a case class, through `DbCodec.derived` (equivalent), and through `DbCodecDeriver.withColumnNameMapper` when a non-default naming strategy is needed.
|
|
39
|
+
|
|
40
|
+
```scala
|
|
41
|
+
import zio.blocks.sql.{DbCodec, DbCodecDeriver, SqlNameMapper}
|
|
42
|
+
import zio.blocks.schema.Schema
|
|
43
|
+
|
|
44
|
+
// 1. Derives clause — most concise
|
|
45
|
+
case class User(userId: Int, fullName: String) derives DbCodec
|
|
46
|
+
DbCodec[User].columns
|
|
47
|
+
// res0: IndexedSeq[String] = Vector("user_id", "full_name")
|
|
48
|
+
|
|
49
|
+
// 2. DbCodec.derived — equivalent, explicit
|
|
50
|
+
case class Event(eventId: Long, eventType: String)
|
|
51
|
+
object Event { implicit val schema: Schema[Event] = Schema.derived }
|
|
52
|
+
val eventCodec = DbCodec.derived[Event]
|
|
53
|
+
// eventCodec: DbCodec[Event] = zio.blocks.sql.DbCodecDeriver$$anon$20@66f22c33
|
|
54
|
+
eventCodec.columns
|
|
55
|
+
// res1: IndexedSeq[String] = Vector("event_id", "event_type")
|
|
56
|
+
|
|
57
|
+
// 3. withColumnNameMapper — use Identity when your DB already uses camelCase
|
|
58
|
+
case class Widget(widgetId: Int, widgetName: String)
|
|
59
|
+
object Widget { implicit val schema: Schema[Widget] = Schema.derived }
|
|
60
|
+
|
|
61
|
+
val identityDeriver = DbCodecDeriver.withColumnNameMapper(SqlNameMapper.Identity)
|
|
62
|
+
// identityDeriver: DbCodecDeriver = zio.blocks.sql.DbCodecDeriver@33803e98
|
|
63
|
+
val widgetCodec = Widget.schema.deriving(identityDeriver).derive
|
|
64
|
+
// widgetCodec: DbCodec[Widget] = zio.blocks.sql.DbCodecDeriver$$anon$20@35131fba
|
|
65
|
+
widgetCodec.columns
|
|
66
|
+
// res2: IndexedSeq[String] = Vector("widgetId", "widgetName")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## See Also
|
|
70
|
+
|
|
71
|
+
For the naming strategy that `DbCodecDeriver` applies, see [SqlNameMapper](./sql-name-mapper.md). For the `DbCodec` type that derivation produces, see [DbCodec](./db-codec.md). For `Table.derived` and DDL generation, see [Table](./table.md).
|