@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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: field
|
|
3
|
+
title: "Field"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`Field` represents a class field (constructor parameter) in the IR. Combine a name with a type reference, optionally adding default values and modifiers.
|
|
7
|
+
|
|
8
|
+
## Use Cases
|
|
9
|
+
|
|
10
|
+
- Defining constructor parameters for case classes
|
|
11
|
+
- Specifying fields in abstract classes
|
|
12
|
+
- Modeling data structure fields with optional defaults
|
|
13
|
+
|
|
14
|
+
## Construction
|
|
15
|
+
|
|
16
|
+
Build a field with a name and type:
|
|
17
|
+
|
|
18
|
+
```scala
|
|
19
|
+
import zio.blocks.codegen.ir._
|
|
20
|
+
|
|
21
|
+
val id = Field("id", TypeRef.Long)
|
|
22
|
+
val name = Field("name", TypeRef.String)
|
|
23
|
+
val email = Field("email", TypeRef.optional(TypeRef.String))
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
With default value:
|
|
27
|
+
|
|
28
|
+
```scala
|
|
29
|
+
import zio.blocks.codegen.ir._
|
|
30
|
+
|
|
31
|
+
val timeout = Field("timeout", TypeRef.Long, defaultValue = Some("5000L"))
|
|
32
|
+
val retries = Field("retries", TypeRef.Int, defaultValue = Some("3"))
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
With annotations:
|
|
36
|
+
|
|
37
|
+
```scala
|
|
38
|
+
import zio.blocks.codegen.ir._
|
|
39
|
+
|
|
40
|
+
val annotated = Field(
|
|
41
|
+
"id",
|
|
42
|
+
TypeRef.Long,
|
|
43
|
+
annotations = List(Annotation("id"))
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Key Operations
|
|
48
|
+
|
|
49
|
+
All `Field` instances support these operations:
|
|
50
|
+
|
|
51
|
+
### Accessing Components
|
|
52
|
+
|
|
53
|
+
Extract parts of a field:
|
|
54
|
+
|
|
55
|
+
```scala
|
|
56
|
+
import zio.blocks.codegen.ir._
|
|
57
|
+
|
|
58
|
+
val field = Field("age", TypeRef.Int, defaultValue = Some("0"))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
View field properties:
|
|
62
|
+
|
|
63
|
+
```scala
|
|
64
|
+
import zio.blocks.codegen.ir._
|
|
65
|
+
|
|
66
|
+
field.name // "age"
|
|
67
|
+
// res4: String = "age"
|
|
68
|
+
field.typeRef // TypeRef.Int
|
|
69
|
+
// res5: TypeRef = TypeRef(name = "Int", typeArgs = List())
|
|
70
|
+
field.defaultValue // Some("0")
|
|
71
|
+
// res6: Option[String] = Some("0")
|
|
72
|
+
field.annotations // List[Annotation]
|
|
73
|
+
// res7: List[Annotation] = List()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Building with Copy
|
|
77
|
+
|
|
78
|
+
Modify a field using copy:
|
|
79
|
+
|
|
80
|
+
```scala
|
|
81
|
+
import zio.blocks.codegen.ir._
|
|
82
|
+
|
|
83
|
+
val field = Field("age", TypeRef.Int, defaultValue = Some("0"))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Apply modifications:
|
|
87
|
+
|
|
88
|
+
```scala
|
|
89
|
+
import zio.blocks.codegen.ir._
|
|
90
|
+
|
|
91
|
+
val updated = field.copy(
|
|
92
|
+
defaultValue = Some("18"),
|
|
93
|
+
annotations = List(Annotation("min"))
|
|
94
|
+
)
|
|
95
|
+
// updated: Field = Field(
|
|
96
|
+
// name = "age",
|
|
97
|
+
// typeRef = TypeRef(name = "Int", typeArgs = List()),
|
|
98
|
+
// defaultValue = Some("18"),
|
|
99
|
+
// annotations = List(Annotation(name = "min", args = List())),
|
|
100
|
+
// doc = None
|
|
101
|
+
// )
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Examples
|
|
105
|
+
|
|
106
|
+
These examples show practical usage patterns for `Field`:
|
|
107
|
+
|
|
108
|
+
### Example 1: Simple Fields
|
|
109
|
+
|
|
110
|
+
Create fields for a case class:
|
|
111
|
+
|
|
112
|
+
```scala
|
|
113
|
+
import zio.blocks.codegen.ir._
|
|
114
|
+
import zio.blocks.codegen.emit._
|
|
115
|
+
|
|
116
|
+
val order = CaseClass(
|
|
117
|
+
name = "Order",
|
|
118
|
+
fields = List(
|
|
119
|
+
Field("id", TypeRef.Long),
|
|
120
|
+
Field("customerId", TypeRef.String),
|
|
121
|
+
Field("total", TypeRef("BigDecimal")),
|
|
122
|
+
Field("status", TypeRef.String)
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
val file = ScalaFile(
|
|
127
|
+
packageDecl = PackageDecl("com.shop"),
|
|
128
|
+
types = List(order)
|
|
129
|
+
)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Emits:
|
|
133
|
+
|
|
134
|
+
```scala
|
|
135
|
+
import zio.blocks.codegen.emit._
|
|
136
|
+
|
|
137
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
138
|
+
// res10: String = """package com.shop
|
|
139
|
+
//
|
|
140
|
+
// case class Order(
|
|
141
|
+
// id: Long,
|
|
142
|
+
// customerId: String,
|
|
143
|
+
// total: BigDecimal,
|
|
144
|
+
// status: String,
|
|
145
|
+
// )
|
|
146
|
+
// """
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Example 2: Fields with Defaults
|
|
150
|
+
|
|
151
|
+
Case class fields with default values:
|
|
152
|
+
|
|
153
|
+
```scala
|
|
154
|
+
import zio.blocks.codegen.ir._
|
|
155
|
+
import zio.blocks.codegen.emit._
|
|
156
|
+
|
|
157
|
+
val config = CaseClass(
|
|
158
|
+
name = "DatabaseConfig",
|
|
159
|
+
fields = List(
|
|
160
|
+
Field("host", TypeRef.String, defaultValue = Some("\"localhost\"")),
|
|
161
|
+
Field("port", TypeRef.Int, defaultValue = Some("5432")),
|
|
162
|
+
Field("timeout", TypeRef.Long, defaultValue = Some("30000L"))
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
val file = ScalaFile(
|
|
167
|
+
packageDecl = PackageDecl("com.example"),
|
|
168
|
+
types = List(config)
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Emits:
|
|
173
|
+
|
|
174
|
+
```scala
|
|
175
|
+
import zio.blocks.codegen.emit._
|
|
176
|
+
|
|
177
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
178
|
+
// res12: String = """package com.example
|
|
179
|
+
//
|
|
180
|
+
// case class DatabaseConfig(
|
|
181
|
+
// host: String = "localhost",
|
|
182
|
+
// port: Int = 5432,
|
|
183
|
+
// timeout: Long = 30000L,
|
|
184
|
+
// )
|
|
185
|
+
// """
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Example 3: Optional and Collection Fields
|
|
189
|
+
|
|
190
|
+
Fields with generic types:
|
|
191
|
+
|
|
192
|
+
```scala
|
|
193
|
+
import zio.blocks.codegen.ir._
|
|
194
|
+
import zio.blocks.codegen.emit._
|
|
195
|
+
|
|
196
|
+
val article = CaseClass(
|
|
197
|
+
name = "Article",
|
|
198
|
+
fields = List(
|
|
199
|
+
Field("title", TypeRef.String),
|
|
200
|
+
Field("content", TypeRef.String),
|
|
201
|
+
Field("author", TypeRef.optional(TypeRef.String)),
|
|
202
|
+
Field("tags", TypeRef.list(TypeRef.String)),
|
|
203
|
+
Field("metadata", TypeRef.map(TypeRef.String, TypeRef.String))
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
val file = ScalaFile(
|
|
208
|
+
packageDecl = PackageDecl("com.example"),
|
|
209
|
+
types = List(article)
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Emits:
|
|
214
|
+
|
|
215
|
+
```scala
|
|
216
|
+
import zio.blocks.codegen.emit._
|
|
217
|
+
|
|
218
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
219
|
+
// res14: String = """package com.example
|
|
220
|
+
//
|
|
221
|
+
// case class Article(
|
|
222
|
+
// title: String,
|
|
223
|
+
// content: String,
|
|
224
|
+
// author: Option[String],
|
|
225
|
+
// tags: List[String],
|
|
226
|
+
// metadata: Map[String, String],
|
|
227
|
+
// )
|
|
228
|
+
// """
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Example 4: Nested Generic Types
|
|
232
|
+
|
|
233
|
+
Complex field types:
|
|
234
|
+
|
|
235
|
+
```scala
|
|
236
|
+
import zio.blocks.codegen.ir._
|
|
237
|
+
import zio.blocks.codegen.emit._
|
|
238
|
+
|
|
239
|
+
val response = CaseClass(
|
|
240
|
+
name = "Response",
|
|
241
|
+
fields = List(
|
|
242
|
+
Field(
|
|
243
|
+
"data",
|
|
244
|
+
TypeRef("Option", List(
|
|
245
|
+
TypeRef("List", List(TypeRef.String))
|
|
246
|
+
))
|
|
247
|
+
),
|
|
248
|
+
Field(
|
|
249
|
+
"errors",
|
|
250
|
+
TypeRef("List", List(
|
|
251
|
+
TypeRef("Map", List(TypeRef.String, TypeRef.String))
|
|
252
|
+
))
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
val file = ScalaFile(
|
|
258
|
+
packageDecl = PackageDecl("com.example"),
|
|
259
|
+
types = List(response)
|
|
260
|
+
)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Emits:
|
|
264
|
+
|
|
265
|
+
```scala
|
|
266
|
+
import zio.blocks.codegen.emit._
|
|
267
|
+
|
|
268
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
269
|
+
// res16: String = """package com.example
|
|
270
|
+
//
|
|
271
|
+
// case class Response(
|
|
272
|
+
// data: Option[List[String]],
|
|
273
|
+
// errors: List[Map[String, String]],
|
|
274
|
+
// )
|
|
275
|
+
// """
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Example 5: Fields with Type Parameters
|
|
279
|
+
|
|
280
|
+
Fields using generic type variables:
|
|
281
|
+
|
|
282
|
+
```scala
|
|
283
|
+
import zio.blocks.codegen.ir._
|
|
284
|
+
import zio.blocks.codegen.emit._
|
|
285
|
+
|
|
286
|
+
val page = CaseClass(
|
|
287
|
+
name = "Page",
|
|
288
|
+
fields = List(
|
|
289
|
+
Field("items", TypeRef("List", List(TypeRef("T")))),
|
|
290
|
+
Field("total", TypeRef.Long),
|
|
291
|
+
Field("pageSize", TypeRef.Int)
|
|
292
|
+
),
|
|
293
|
+
typeParams = List(TypeParam("T"))
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
val file = ScalaFile(
|
|
297
|
+
packageDecl = PackageDecl("com.example"),
|
|
298
|
+
types = List(page)
|
|
299
|
+
)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Emits:
|
|
303
|
+
|
|
304
|
+
```scala
|
|
305
|
+
import zio.blocks.codegen.emit._
|
|
306
|
+
|
|
307
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
308
|
+
// res18: String = """package com.example
|
|
309
|
+
//
|
|
310
|
+
// case class Page[T](
|
|
311
|
+
// items: List[T],
|
|
312
|
+
// total: Long,
|
|
313
|
+
// pageSize: Int,
|
|
314
|
+
// )
|
|
315
|
+
// """
|
|
316
|
+
```
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: index
|
|
3
|
+
title: "Code Generation"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`zio-blocks-codegen` is a **generic, domain-agnostic Scala code generation library**. It provides an intermediate representation (IR) for building type-safe models of Scala code structures, and a pure emitter that generates well-formatted Scala source files from those models.
|
|
7
|
+
|
|
8
|
+
Core types: `ScalaFile`, `TypeDefinition`, `CaseClass`, `SealedTrait`, `Enum`, `Field`, `TypeRef`, `Method`, `Annotation`.
|
|
9
|
+
|
|
10
|
+
Here's the structure of two core types:
|
|
11
|
+
|
|
12
|
+
```scala
|
|
13
|
+
// IR models the structure of Scala code
|
|
14
|
+
case class ScalaFile(
|
|
15
|
+
packageDecl: PackageDecl,
|
|
16
|
+
imports: List[Import] = Nil,
|
|
17
|
+
types: List[TypeDefinition] = Nil
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
case class CaseClass(
|
|
21
|
+
name: String,
|
|
22
|
+
fields: List[Field],
|
|
23
|
+
typeParams: List[TypeParam] = Nil,
|
|
24
|
+
derives: List[String] = Nil,
|
|
25
|
+
// ...
|
|
26
|
+
)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Introduction
|
|
30
|
+
|
|
31
|
+
This module is designed to be a **reusable building block** for any tool that needs to generate Scala code—whether from OpenAPI specifications, Smithy models, Protocol Buffers, JSON Schema, or any other source format.
|
|
32
|
+
|
|
33
|
+
Rather than embedding code generation logic into domain-specific tools, you model your source data in the codegen IR, then emit clean, formatted Scala. This separation enables single source of truth, consistency, and reuse across all generators without cross-coupling.
|
|
34
|
+
|
|
35
|
+
## Motivation
|
|
36
|
+
|
|
37
|
+
Codegen IR exists to solve a specific problem: **many generators produce the same output (Scala code) but reinvent the emission logic.** Before `zio-blocks-codegen`, each generator (OpenAPI, Smithy, Protobuf, etc.) had its own IR and emitter, leading to duplication, bugs, and inconsistency. By extracting IR and emitter into `zio-blocks-codegen`, all generators share one implementation.
|
|
38
|
+
|
|
39
|
+
Before `zio-blocks-codegen`, each generator had its own IR and emitter:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
OpenAPI → Scala Smithy → Scala Protobuf → Scala
|
|
43
|
+
↓ ↓ ↓
|
|
44
|
+
[Custom IR] [Custom IR] [Custom IR]
|
|
45
|
+
↓ ↓ ↓
|
|
46
|
+
[Custom Emitter] [Custom Emitter] [Custom Emitter]
|
|
47
|
+
↓ ↓ ↓
|
|
48
|
+
Scala Code Scala Code Scala Code
|
|
49
|
+
|
|
50
|
+
❌ Duplication: Same problem solved 3 times with different code
|
|
51
|
+
❌ Bugs: Fixes in one place don't help others
|
|
52
|
+
❌ Inconsistency: Different styles, formatting, edge cases
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Now with `zio-blocks-codegen`, all generators converge on a single implementation:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
OpenAPI → Scala Smithy → Scala Protobuf → Scala JSON Schema → Scala
|
|
59
|
+
↓ ↓ ↓ ↓
|
|
60
|
+
└──────────────────┬───────────────────┘────────────────────┘
|
|
61
|
+
↓
|
|
62
|
+
[zio-blocks-codegen]
|
|
63
|
+
↓
|
|
64
|
+
┌──────────┴──────────┐
|
|
65
|
+
↓ ↓
|
|
66
|
+
[IR: Type-safe] [Emitter: Pure function]
|
|
67
|
+
representation (no side effects)
|
|
68
|
+
of Scala code |
|
|
69
|
+
↓ ↓
|
|
70
|
+
└──────────┬──────────┘
|
|
71
|
+
↓
|
|
72
|
+
Scala Code
|
|
73
|
+
|
|
74
|
+
✅ Single source of truth: One well-tested implementation
|
|
75
|
+
✅ Consistency: All generators use the same emitter
|
|
76
|
+
✅ Reusability: Zero coupling between domain-specific tools
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
Add the library to your project:
|
|
82
|
+
|
|
83
|
+
```scala
|
|
84
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-codegen" % "0.0.51"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Supported Scala versions: 2.13.x and 3.x
|
|
88
|
+
|
|
89
|
+
## Overview
|
|
90
|
+
|
|
91
|
+
The module has two key layers:
|
|
92
|
+
1. **IR Layer**: Immutable, strongly-typed models of Scala code structures (files, types, members)
|
|
93
|
+
2. **Emit Layer**: Pure functions that convert IR models to formatted Scala source code
|
|
94
|
+
|
|
95
|
+
### IR Layer (`zio.blocks.codegen.ir`)
|
|
96
|
+
|
|
97
|
+
The intermediate representation captures the essential structure of Scala code:
|
|
98
|
+
|
|
99
|
+
- **File structure**: `ScalaFile`, `PackageDecl`, `Import`
|
|
100
|
+
- **Type definitions**: `CaseClass`, `SealedTrait`, `Trait`, `AbstractClass`, `Enum`, `ObjectDef`, `OpaqueType`, `Newtype`, `TypeAlias`
|
|
101
|
+
- **Members**: `Field`, `Method`, `MethodParam`, `Annotation`, `TypeParam`, `TypeRef`
|
|
102
|
+
- **Composition**: Sealed traits like `TypeDefinition`, `SealedTraitCase`, `EnumCase`, `ObjectMember` for modular type safety
|
|
103
|
+
|
|
104
|
+
Each type is **immutable and strongly typed**. You build the IR by composing these types, then hand off to the emitter.
|
|
105
|
+
|
|
106
|
+
### Emit Layer (`zio.blocks.codegen.emit`)
|
|
107
|
+
|
|
108
|
+
The emitter converts IR to Scala source code:
|
|
109
|
+
|
|
110
|
+
- **`ScalaEmitter`**: Methods to emit any IR node as a formatted string (imports, type definitions, methods, etc.)
|
|
111
|
+
- **`EmitterConfig`**: Configurable formatting (indent width, import sorting, trailing commas, Scala 2 vs. Scala 3 syntax)
|
|
112
|
+
- **No side effects**: Returns strings; your code writes files (or does anything else)
|
|
113
|
+
|
|
114
|
+
## How They Work Together
|
|
115
|
+
|
|
116
|
+
Here's the typical workflow for a code generator:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
1. Parse source format (OpenAPI, Smithy, etc.)
|
|
120
|
+
↓
|
|
121
|
+
2. Build IR models (ScalaFile, CaseClass, SealedTrait, etc.)
|
|
122
|
+
↓
|
|
123
|
+
3. Call ScalaEmitter.emit(file, config)
|
|
124
|
+
↓
|
|
125
|
+
4. ScalaEmitter returns formatted Scala source as String
|
|
126
|
+
↓
|
|
127
|
+
5. Write string to file (or further process it)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Here's an example of building a Scala file with a case class:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
ScalaFile
|
|
134
|
+
├─ packageDecl: PackageDecl("com.example")
|
|
135
|
+
├─ imports: [Import.WildcardImport("zio")]
|
|
136
|
+
└─ types: [
|
|
137
|
+
CaseClass(
|
|
138
|
+
name = "User",
|
|
139
|
+
fields = [
|
|
140
|
+
Field("id", TypeRef.Long),
|
|
141
|
+
Field("name", TypeRef.String),
|
|
142
|
+
Field("email", TypeRef("Option", List(TypeRef.String)))
|
|
143
|
+
]
|
|
144
|
+
)
|
|
145
|
+
]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
When you call `ScalaEmitter.emit(file, config)`, it walks this tree and produces:
|
|
149
|
+
|
|
150
|
+
```scala
|
|
151
|
+
package com.example
|
|
152
|
+
|
|
153
|
+
import zio._
|
|
154
|
+
|
|
155
|
+
case class User(
|
|
156
|
+
id: Long,
|
|
157
|
+
name: String,
|
|
158
|
+
email: Option[String],
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The architecture flows through three layers:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
┌──────────────────────────────────────────────────────────┐
|
|
166
|
+
│ Your Generator (OpenAPI, Smithy, Protobuf, JSON Schema) │
|
|
167
|
+
│ - Parse input format │
|
|
168
|
+
│ - Build IR models (CaseClass, SealedTrait, etc.) │
|
|
169
|
+
└────────────────┬─────────────────────────────────────────┘
|
|
170
|
+
│ ScalaFile (root IR node)
|
|
171
|
+
▼
|
|
172
|
+
┌──────────────────────────────────────────────────────────┐
|
|
173
|
+
│ zio-blocks-codegen IR Layer │
|
|
174
|
+
│ - TypeDefinition (sealed trait) │
|
|
175
|
+
│ - CaseClass, SealedTrait, Enum, ObjectDef, ... │
|
|
176
|
+
│ - Field, Method, TypeRef, Annotation, TypeParam │
|
|
177
|
+
│ - Strongly typed, immutable, composable │
|
|
178
|
+
└────────────────┬─────────────────────────────────────────┘
|
|
179
|
+
│ IR models
|
|
180
|
+
▼
|
|
181
|
+
┌─────────────────────────────────────────────────────────┐
|
|
182
|
+
│ zio-blocks-codegen Emit Layer │
|
|
183
|
+
│ - ScalaEmitter.emit(file, config) → String │
|
|
184
|
+
│ - Supports Scala 3 (enums, derives, * imports) │
|
|
185
|
+
│ - Supports Scala 2 (sealed traits, _ imports, fallback) │
|
|
186
|
+
│ - EmitterConfig: indent, imports, commas, version │
|
|
187
|
+
└────────────────┬────────────────────────────────────────┘
|
|
188
|
+
│ Formatted Scala source
|
|
189
|
+
▼
|
|
190
|
+
(Write to file or further process)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Common Patterns
|
|
194
|
+
|
|
195
|
+
Here are the most common usage patterns:
|
|
196
|
+
|
|
197
|
+
### Pattern 1: Building a Simple Case Class
|
|
198
|
+
|
|
199
|
+
Build a case class with fields and derive clauses:
|
|
200
|
+
|
|
201
|
+
```scala
|
|
202
|
+
import zio.blocks.codegen.ir._
|
|
203
|
+
|
|
204
|
+
val user = CaseClass(
|
|
205
|
+
name = "User",
|
|
206
|
+
fields = List(
|
|
207
|
+
Field("id", TypeRef.Long),
|
|
208
|
+
Field("name", TypeRef.String)
|
|
209
|
+
),
|
|
210
|
+
derives = List("Schema")
|
|
211
|
+
)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Pattern 2: Sealed Trait Hierarchies
|
|
215
|
+
|
|
216
|
+
Model ADTs (algebraic data types) as sealed traits with cases:
|
|
217
|
+
|
|
218
|
+
```scala
|
|
219
|
+
import zio.blocks.codegen.ir._
|
|
220
|
+
|
|
221
|
+
val payment = SealedTrait(
|
|
222
|
+
name = "Payment",
|
|
223
|
+
cases = List(
|
|
224
|
+
SealedTraitCase.CaseClassCase(
|
|
225
|
+
CaseClass(
|
|
226
|
+
"CreditCard",
|
|
227
|
+
List(
|
|
228
|
+
Field("number", TypeRef.String),
|
|
229
|
+
Field("cvv", TypeRef.String)
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
),
|
|
233
|
+
SealedTraitCase.CaseObjectCase("Cash"),
|
|
234
|
+
SealedTraitCase.CaseObjectCase("Check")
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Pattern 3: Generic Types with Type Parameters
|
|
240
|
+
|
|
241
|
+
Define polymorphic types:
|
|
242
|
+
|
|
243
|
+
```scala
|
|
244
|
+
import zio.blocks.codegen.ir._
|
|
245
|
+
|
|
246
|
+
val container = CaseClass(
|
|
247
|
+
name = "Container",
|
|
248
|
+
fields = List(
|
|
249
|
+
Field("value", TypeRef("A"))
|
|
250
|
+
),
|
|
251
|
+
typeParams = List(TypeParam("A"))
|
|
252
|
+
)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Pattern 4: Complete File Structure
|
|
256
|
+
|
|
257
|
+
Assemble a complete Scala file ready for emission:
|
|
258
|
+
|
|
259
|
+
```scala
|
|
260
|
+
import zio.blocks.codegen.ir._
|
|
261
|
+
|
|
262
|
+
val user = CaseClass(
|
|
263
|
+
name = "User",
|
|
264
|
+
fields = List(
|
|
265
|
+
Field("id", TypeRef.Long),
|
|
266
|
+
Field("name", TypeRef.String)
|
|
267
|
+
),
|
|
268
|
+
derives = List("Schema")
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
val payment = SealedTrait(
|
|
272
|
+
name = "Payment",
|
|
273
|
+
cases = List(
|
|
274
|
+
SealedTraitCase.CaseObjectCase("Cash"),
|
|
275
|
+
SealedTraitCase.CaseObjectCase("Card")
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
val file = ScalaFile(
|
|
280
|
+
packageDecl = PackageDecl("com.example"),
|
|
281
|
+
imports = List(
|
|
282
|
+
Import.WildcardImport("zio"),
|
|
283
|
+
Import.SingleImport("zio.blocks.schema", "Schema")
|
|
284
|
+
),
|
|
285
|
+
types = List(user, payment)
|
|
286
|
+
)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Cross-Scala Compatibility
|
|
290
|
+
|
|
291
|
+
The emitter handles both **Scala 3** and **Scala 2** natively:
|
|
292
|
+
|
|
293
|
+
- **Scala 3 features**: Enums, derives clauses, `*` imports, `as` renames, opaque types
|
|
294
|
+
- **Scala 2 fallback**: Sealed traits, `_` imports, `=>` renames, type aliases
|
|
295
|
+
|
|
296
|
+
You configure the output via `EmitterConfig`:
|
|
297
|
+
|
|
298
|
+
```scala
|
|
299
|
+
import zio.blocks.codegen.emit._
|
|
300
|
+
|
|
301
|
+
val config = EmitterConfig(
|
|
302
|
+
scala3Syntax = true, // true for Scala 3, false for Scala 2
|
|
303
|
+
indentWidth = 2,
|
|
304
|
+
sortImports = true,
|
|
305
|
+
trailingCommas = true
|
|
306
|
+
)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Design Philosophy
|
|
310
|
+
|
|
311
|
+
Three principles guide codegen IR:
|
|
312
|
+
|
|
313
|
+
1. **Generic**: No domain-specific logic (OpenAPI-specific stuff stays in `zio-blocks-openapi`)
|
|
314
|
+
2. **Pure**: No side effects—just IR models and string emission
|
|
315
|
+
3. **Self-contained**: Zero external dependencies, works everywhere
|
|
316
|
+
|
|
317
|
+
This makes it a safe, reliable foundation for any Scala code generator.
|