@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,664 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: examples
|
|
3
|
+
title: "Complete Examples"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
This page shows complete, runnable examples demonstrating realistic code generation workflows. Each example builds IR models from scratch and emits formatted Scala code.
|
|
7
|
+
|
|
8
|
+
## Example 1: Simple Domain Model
|
|
9
|
+
|
|
10
|
+
Build a complete domain model with a case class and sealed trait:
|
|
11
|
+
|
|
12
|
+
```scala
|
|
13
|
+
import zio.blocks.codegen.ir._
|
|
14
|
+
import zio.blocks.codegen.emit._
|
|
15
|
+
|
|
16
|
+
// Define error types
|
|
17
|
+
val error = SealedTrait(
|
|
18
|
+
name = "Error",
|
|
19
|
+
cases = List(
|
|
20
|
+
SealedTraitCase.CaseObjectCase("NotFound"),
|
|
21
|
+
SealedTraitCase.CaseObjectCase("Unauthorized"),
|
|
22
|
+
SealedTraitCase.CaseClassCase(
|
|
23
|
+
CaseClass("ValidationError", List(
|
|
24
|
+
Field("field", TypeRef.String),
|
|
25
|
+
Field("message", TypeRef.String)
|
|
26
|
+
))
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
// Define a user
|
|
32
|
+
val user = CaseClass(
|
|
33
|
+
name = "User",
|
|
34
|
+
fields = List(
|
|
35
|
+
Field("id", TypeRef.Long),
|
|
36
|
+
Field("email", TypeRef.String),
|
|
37
|
+
Field("name", TypeRef.String),
|
|
38
|
+
Field("active", TypeRef.Boolean, defaultValue = Some("true"))
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
// Assemble into a file
|
|
43
|
+
val file = ScalaFile(
|
|
44
|
+
packageDecl = PackageDecl("com.example.domain"),
|
|
45
|
+
imports = List(
|
|
46
|
+
Import.WildcardImport("zio")
|
|
47
|
+
),
|
|
48
|
+
types = List(error, user)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
val config = EmitterConfig(indentWidth = 2)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This generates:
|
|
55
|
+
|
|
56
|
+
```scala
|
|
57
|
+
import zio.blocks.codegen.emit._
|
|
58
|
+
val code = ScalaEmitter.emit(file, config)
|
|
59
|
+
// code: String = """package com.example.domain
|
|
60
|
+
//
|
|
61
|
+
// import zio.*
|
|
62
|
+
//
|
|
63
|
+
// sealed trait Error
|
|
64
|
+
//
|
|
65
|
+
// object Error {
|
|
66
|
+
// case object NotFound extends Error
|
|
67
|
+
// case object Unauthorized extends Error
|
|
68
|
+
// case class ValidationError(
|
|
69
|
+
// field: String,
|
|
70
|
+
// message: String,
|
|
71
|
+
// ) extends Error
|
|
72
|
+
// }
|
|
73
|
+
//
|
|
74
|
+
// case class User(
|
|
75
|
+
// id: Long,
|
|
76
|
+
// email: String,
|
|
77
|
+
// name: String,
|
|
78
|
+
// active: Boolean = true,
|
|
79
|
+
// )
|
|
80
|
+
// """
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Example 2: Generic Container Types
|
|
84
|
+
|
|
85
|
+
Build polymorphic types with type parameters:
|
|
86
|
+
|
|
87
|
+
```scala
|
|
88
|
+
import zio.blocks.codegen.ir._
|
|
89
|
+
import zio.blocks.codegen.emit._
|
|
90
|
+
|
|
91
|
+
// A generic wrapper
|
|
92
|
+
val container = CaseClass(
|
|
93
|
+
name = "Container",
|
|
94
|
+
fields = List(
|
|
95
|
+
Field("value", TypeRef("T")),
|
|
96
|
+
Field("metadata", TypeRef("Map", List(TypeRef.String, TypeRef.String)))
|
|
97
|
+
),
|
|
98
|
+
typeParams = List(TypeParam("T")),
|
|
99
|
+
derives = List("Show")
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// A generic result type
|
|
103
|
+
val result = SealedTrait(
|
|
104
|
+
name = "Result",
|
|
105
|
+
typeParams = List(TypeParam("A")),
|
|
106
|
+
cases = List(
|
|
107
|
+
SealedTraitCase.CaseClassCase(
|
|
108
|
+
CaseClass("Success", List(
|
|
109
|
+
Field("value", TypeRef("A"))
|
|
110
|
+
), typeParams = List(TypeParam("A")))
|
|
111
|
+
),
|
|
112
|
+
SealedTraitCase.CaseClassCase(
|
|
113
|
+
CaseClass("Failure", List(
|
|
114
|
+
Field("error", TypeRef.String)
|
|
115
|
+
))
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
val file = ScalaFile(
|
|
121
|
+
packageDecl = PackageDecl("com.example.types"),
|
|
122
|
+
types = List(container, result)
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Generates:
|
|
127
|
+
|
|
128
|
+
```scala
|
|
129
|
+
import zio.blocks.codegen.emit._
|
|
130
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
131
|
+
// res1: String = """package com.example.types
|
|
132
|
+
//
|
|
133
|
+
// case class Container[T](
|
|
134
|
+
// value: T,
|
|
135
|
+
// metadata: Map[String, String],
|
|
136
|
+
// ) derives Show
|
|
137
|
+
//
|
|
138
|
+
// sealed trait Result[A]
|
|
139
|
+
//
|
|
140
|
+
// object Result {
|
|
141
|
+
// case class Success[A](
|
|
142
|
+
// value: A,
|
|
143
|
+
// ) extends Result[A]
|
|
144
|
+
// case class Failure(
|
|
145
|
+
// error: String,
|
|
146
|
+
// ) extends Result[A]
|
|
147
|
+
// }
|
|
148
|
+
// """
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Example 3: API Request/Response Models
|
|
152
|
+
|
|
153
|
+
Generate HTTP API models from scratch:
|
|
154
|
+
|
|
155
|
+
```scala
|
|
156
|
+
import zio.blocks.codegen.ir._
|
|
157
|
+
import zio.blocks.codegen.emit._
|
|
158
|
+
|
|
159
|
+
// Request model
|
|
160
|
+
val createUserReq = CaseClass(
|
|
161
|
+
name = "CreateUserRequest",
|
|
162
|
+
fields = List(
|
|
163
|
+
Field("email", TypeRef.String),
|
|
164
|
+
Field("name", TypeRef.String),
|
|
165
|
+
Field("password", TypeRef.String)
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
// Response model
|
|
170
|
+
val user = CaseClass(
|
|
171
|
+
name = "User",
|
|
172
|
+
fields = List(
|
|
173
|
+
Field("id", TypeRef.Long),
|
|
174
|
+
Field("email", TypeRef.String),
|
|
175
|
+
Field("name", TypeRef.String),
|
|
176
|
+
Field("createdAt", TypeRef.String)
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
// Error responses
|
|
181
|
+
val apiError = SealedTrait(
|
|
182
|
+
name = "ApiError",
|
|
183
|
+
cases = List(
|
|
184
|
+
SealedTraitCase.CaseClassCase(
|
|
185
|
+
CaseClass("BadRequest", List(
|
|
186
|
+
Field("message", TypeRef.String)
|
|
187
|
+
))
|
|
188
|
+
),
|
|
189
|
+
SealedTraitCase.CaseClassCase(
|
|
190
|
+
CaseClass("Conflict", List(
|
|
191
|
+
Field("resource", TypeRef.String),
|
|
192
|
+
Field("details", TypeRef.String)
|
|
193
|
+
))
|
|
194
|
+
),
|
|
195
|
+
SealedTraitCase.CaseObjectCase("InternalServerError")
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
val file = ScalaFile(
|
|
200
|
+
packageDecl = PackageDecl("com.example.api.models"),
|
|
201
|
+
types = List(createUserReq, user, apiError)
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Generates:
|
|
206
|
+
|
|
207
|
+
```scala
|
|
208
|
+
import zio.blocks.codegen.emit._
|
|
209
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
210
|
+
// res3: String = """package com.example.api.models
|
|
211
|
+
//
|
|
212
|
+
// case class CreateUserRequest(
|
|
213
|
+
// email: String,
|
|
214
|
+
// name: String,
|
|
215
|
+
// password: String,
|
|
216
|
+
// )
|
|
217
|
+
//
|
|
218
|
+
// case class User(
|
|
219
|
+
// id: Long,
|
|
220
|
+
// email: String,
|
|
221
|
+
// name: String,
|
|
222
|
+
// createdAt: String,
|
|
223
|
+
// )
|
|
224
|
+
//
|
|
225
|
+
// sealed trait ApiError
|
|
226
|
+
//
|
|
227
|
+
// object ApiError {
|
|
228
|
+
// case class BadRequest(
|
|
229
|
+
// message: String,
|
|
230
|
+
// ) extends ApiError
|
|
231
|
+
// case class Conflict(
|
|
232
|
+
// resource: String,
|
|
233
|
+
// details: String,
|
|
234
|
+
// ) extends ApiError
|
|
235
|
+
// case object InternalServerError extends ApiError
|
|
236
|
+
// }
|
|
237
|
+
// """
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Example 4: Cross-Scala Version Generation
|
|
241
|
+
|
|
242
|
+
Emit code for both Scala 3 and Scala 2:
|
|
243
|
+
|
|
244
|
+
```scala
|
|
245
|
+
import zio.blocks.codegen.ir._
|
|
246
|
+
import zio.blocks.codegen.emit._
|
|
247
|
+
|
|
248
|
+
// An enum type
|
|
249
|
+
val status = Enum(
|
|
250
|
+
name = "OrderStatus",
|
|
251
|
+
cases = List(
|
|
252
|
+
EnumCase.SimpleCase("Pending"),
|
|
253
|
+
EnumCase.SimpleCase("Shipped"),
|
|
254
|
+
EnumCase.SimpleCase("Delivered"),
|
|
255
|
+
EnumCase.SimpleCase("Cancelled")
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
val file = ScalaFile(
|
|
260
|
+
packageDecl = PackageDecl("com.shop.models"),
|
|
261
|
+
types = List(status)
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
// Generate for Scala 3
|
|
265
|
+
val scala3Code = ScalaEmitter.emit(
|
|
266
|
+
file,
|
|
267
|
+
EmitterConfig(scala3Syntax = true)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
// Generate for Scala 2
|
|
271
|
+
val scala2Code = ScalaEmitter.emit(
|
|
272
|
+
file,
|
|
273
|
+
EmitterConfig(scala3Syntax = false)
|
|
274
|
+
)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Scala 3 output:
|
|
278
|
+
|
|
279
|
+
```scala
|
|
280
|
+
import zio.blocks.codegen.emit._
|
|
281
|
+
scala3Code
|
|
282
|
+
// res5: String = """package com.shop.models
|
|
283
|
+
//
|
|
284
|
+
// enum OrderStatus {
|
|
285
|
+
// case Pending, Shipped, Delivered, Cancelled
|
|
286
|
+
// }
|
|
287
|
+
// """
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Scala 2 output:
|
|
291
|
+
|
|
292
|
+
```scala
|
|
293
|
+
import zio.blocks.codegen.emit._
|
|
294
|
+
scala2Code
|
|
295
|
+
// res6: String = """package com.shop.models
|
|
296
|
+
//
|
|
297
|
+
// sealed trait OrderStatus
|
|
298
|
+
//
|
|
299
|
+
// object OrderStatus {
|
|
300
|
+
// case object Pending extends OrderStatus
|
|
301
|
+
// case object Shipped extends OrderStatus
|
|
302
|
+
// case object Delivered extends OrderStatus
|
|
303
|
+
// case object Cancelled extends OrderStatus
|
|
304
|
+
// }
|
|
305
|
+
// """
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Example 5: Object with Static Methods and Values
|
|
309
|
+
|
|
310
|
+
Generate a companion object with utility methods:
|
|
311
|
+
|
|
312
|
+
```scala
|
|
313
|
+
import zio.blocks.codegen.ir._
|
|
314
|
+
import zio.blocks.codegen.emit._
|
|
315
|
+
|
|
316
|
+
val config = CaseClass(
|
|
317
|
+
name = "AppConfig",
|
|
318
|
+
fields = List(
|
|
319
|
+
Field("host", TypeRef.String),
|
|
320
|
+
Field("port", TypeRef.Int),
|
|
321
|
+
Field("timeout", TypeRef.Long)
|
|
322
|
+
),
|
|
323
|
+
companion = Some(
|
|
324
|
+
CompanionObject(
|
|
325
|
+
members = List(
|
|
326
|
+
ObjectMember.ValMember(
|
|
327
|
+
"Default",
|
|
328
|
+
TypeRef("AppConfig"),
|
|
329
|
+
"AppConfig(\"localhost\", 8080, 30000L)"
|
|
330
|
+
),
|
|
331
|
+
ObjectMember.ValMember(
|
|
332
|
+
"Production",
|
|
333
|
+
TypeRef("AppConfig"),
|
|
334
|
+
"AppConfig(\"api.example.com\", 443, 60000L)"
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
),
|
|
339
|
+
derives = List("Show")
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
val file = ScalaFile(
|
|
343
|
+
packageDecl = PackageDecl("com.example.config"),
|
|
344
|
+
types = List(config)
|
|
345
|
+
)
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Generates:
|
|
349
|
+
|
|
350
|
+
```scala
|
|
351
|
+
import zio.blocks.codegen.emit._
|
|
352
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
353
|
+
// res8: String = """package com.example.config
|
|
354
|
+
//
|
|
355
|
+
// case class AppConfig(
|
|
356
|
+
// host: String,
|
|
357
|
+
// port: Int,
|
|
358
|
+
// timeout: Long,
|
|
359
|
+
// ) derives Show
|
|
360
|
+
//
|
|
361
|
+
// object AppConfig {
|
|
362
|
+
// val Default: AppConfig = AppConfig("localhost", 8080, 30000L)
|
|
363
|
+
// val Production: AppConfig = AppConfig("api.example.com", 443, 60000L)
|
|
364
|
+
// }
|
|
365
|
+
// """
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Example 6: Complex Nested Types
|
|
369
|
+
|
|
370
|
+
Model deeply nested generic structures:
|
|
371
|
+
|
|
372
|
+
```scala
|
|
373
|
+
import zio.blocks.codegen.ir._
|
|
374
|
+
import zio.blocks.codegen.emit._
|
|
375
|
+
|
|
376
|
+
// Pagination wrapper
|
|
377
|
+
val page = CaseClass(
|
|
378
|
+
name = "Page",
|
|
379
|
+
fields = List(
|
|
380
|
+
Field(
|
|
381
|
+
"items",
|
|
382
|
+
TypeRef("List", List(TypeRef("T")))
|
|
383
|
+
),
|
|
384
|
+
Field("total", TypeRef.Long),
|
|
385
|
+
Field("page", TypeRef.Int),
|
|
386
|
+
Field("pageSize", TypeRef.Int),
|
|
387
|
+
Field(
|
|
388
|
+
"errors",
|
|
389
|
+
TypeRef("List", List(
|
|
390
|
+
TypeRef("Map", List(
|
|
391
|
+
TypeRef.String,
|
|
392
|
+
TypeRef.String
|
|
393
|
+
))
|
|
394
|
+
))
|
|
395
|
+
)
|
|
396
|
+
),
|
|
397
|
+
typeParams = List(TypeParam("T"))
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
val file = ScalaFile(
|
|
401
|
+
packageDecl = PackageDecl("com.example.pagination"),
|
|
402
|
+
imports = List(
|
|
403
|
+
Import.WildcardImport("scala.collection")
|
|
404
|
+
),
|
|
405
|
+
types = List(page)
|
|
406
|
+
)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Generates:
|
|
410
|
+
|
|
411
|
+
```scala
|
|
412
|
+
import zio.blocks.codegen.emit._
|
|
413
|
+
ScalaEmitter.emit(file, EmitterConfig(indentWidth = 2))
|
|
414
|
+
// res10: String = """package com.example.pagination
|
|
415
|
+
//
|
|
416
|
+
// import scala.collection.*
|
|
417
|
+
//
|
|
418
|
+
// case class Page[T](
|
|
419
|
+
// items: List[T],
|
|
420
|
+
// total: Long,
|
|
421
|
+
// page: Int,
|
|
422
|
+
// pageSize: Int,
|
|
423
|
+
// errors: List[Map[String, String]],
|
|
424
|
+
// )
|
|
425
|
+
// """
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Example 7: Building Files Incrementally
|
|
429
|
+
|
|
430
|
+
Add types to a file step by step:
|
|
431
|
+
|
|
432
|
+
```scala
|
|
433
|
+
import zio.blocks.codegen.ir._
|
|
434
|
+
import zio.blocks.codegen.emit._
|
|
435
|
+
|
|
436
|
+
// Build types
|
|
437
|
+
val user = CaseClass("User", List(Field("id", TypeRef.Long)))
|
|
438
|
+
val order = CaseClass("Order", List(
|
|
439
|
+
Field("id", TypeRef.Long),
|
|
440
|
+
Field("userId", TypeRef.Long)
|
|
441
|
+
))
|
|
442
|
+
val status = Enum(
|
|
443
|
+
"OrderStatus",
|
|
444
|
+
cases = List(
|
|
445
|
+
EnumCase.SimpleCase("Pending"),
|
|
446
|
+
EnumCase.SimpleCase("Complete")
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
// Assemble into a file
|
|
451
|
+
val file = ScalaFile(
|
|
452
|
+
packageDecl = PackageDecl("com.example"),
|
|
453
|
+
imports = List(Import.WildcardImport("zio")),
|
|
454
|
+
types = List(user, order, status)
|
|
455
|
+
)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Generates:
|
|
459
|
+
|
|
460
|
+
```scala
|
|
461
|
+
import zio.blocks.codegen.emit._
|
|
462
|
+
ScalaEmitter.emit(file, EmitterConfig())
|
|
463
|
+
// res12: String = """package com.example
|
|
464
|
+
//
|
|
465
|
+
// import zio.*
|
|
466
|
+
//
|
|
467
|
+
// case class User(
|
|
468
|
+
// id: Long,
|
|
469
|
+
// )
|
|
470
|
+
//
|
|
471
|
+
// case class Order(
|
|
472
|
+
// id: Long,
|
|
473
|
+
// userId: Long,
|
|
474
|
+
// )
|
|
475
|
+
//
|
|
476
|
+
// enum OrderStatus {
|
|
477
|
+
// case Pending, Complete
|
|
478
|
+
// }
|
|
479
|
+
// """
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Example 8: Multi-File Generation
|
|
483
|
+
|
|
484
|
+
Generate multiple files from a single data model:
|
|
485
|
+
|
|
486
|
+
```scala
|
|
487
|
+
import zio.blocks.codegen.ir._
|
|
488
|
+
import zio.blocks.codegen.emit._
|
|
489
|
+
|
|
490
|
+
val config = EmitterConfig()
|
|
491
|
+
|
|
492
|
+
// Models file
|
|
493
|
+
val modelsFile = ScalaFile(
|
|
494
|
+
packageDecl = PackageDecl("com.example.models"),
|
|
495
|
+
types = List(
|
|
496
|
+
CaseClass("User", List(
|
|
497
|
+
Field("id", TypeRef.Long),
|
|
498
|
+
Field("name", TypeRef.String)
|
|
499
|
+
)),
|
|
500
|
+
CaseClass("Product", List(
|
|
501
|
+
Field("id", TypeRef.Long),
|
|
502
|
+
Field("price", TypeRef("BigDecimal"))
|
|
503
|
+
))
|
|
504
|
+
)
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
// Errors file
|
|
508
|
+
val errorsFile = ScalaFile(
|
|
509
|
+
packageDecl = PackageDecl("com.example.errors"),
|
|
510
|
+
types = List(
|
|
511
|
+
SealedTrait(
|
|
512
|
+
"DomainError",
|
|
513
|
+
cases = List(
|
|
514
|
+
SealedTraitCase.CaseObjectCase("NotFound"),
|
|
515
|
+
SealedTraitCase.CaseObjectCase("Unauthorized")
|
|
516
|
+
)
|
|
517
|
+
)
|
|
518
|
+
)
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
// Emit both
|
|
522
|
+
val modelsCode = ScalaEmitter.emit(modelsFile, config)
|
|
523
|
+
val errorsCode = ScalaEmitter.emit(errorsFile, config)
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Models output:
|
|
527
|
+
|
|
528
|
+
```scala
|
|
529
|
+
import zio.blocks.codegen.emit._
|
|
530
|
+
modelsCode
|
|
531
|
+
// res14: String = """package com.example.models
|
|
532
|
+
//
|
|
533
|
+
// case class User(
|
|
534
|
+
// id: Long,
|
|
535
|
+
// name: String,
|
|
536
|
+
// )
|
|
537
|
+
//
|
|
538
|
+
// case class Product(
|
|
539
|
+
// id: Long,
|
|
540
|
+
// price: BigDecimal,
|
|
541
|
+
// )
|
|
542
|
+
// """
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Errors output:
|
|
546
|
+
|
|
547
|
+
```scala
|
|
548
|
+
import zio.blocks.codegen.emit._
|
|
549
|
+
errorsCode
|
|
550
|
+
// res15: String = """package com.example.errors
|
|
551
|
+
//
|
|
552
|
+
// sealed trait DomainError
|
|
553
|
+
//
|
|
554
|
+
// object DomainError {
|
|
555
|
+
// case object NotFound extends DomainError
|
|
556
|
+
// case object Unauthorized extends DomainError
|
|
557
|
+
// }
|
|
558
|
+
// """
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
## Key Patterns
|
|
562
|
+
|
|
563
|
+
These patterns recur across all code generation workflows:
|
|
564
|
+
|
|
565
|
+
### Building Incrementally
|
|
566
|
+
|
|
567
|
+
When generating code, you'll often build types incrementally:
|
|
568
|
+
|
|
569
|
+
```scala
|
|
570
|
+
import zio.blocks.codegen.ir._
|
|
571
|
+
|
|
572
|
+
val types = List(
|
|
573
|
+
CaseClass("User", List()),
|
|
574
|
+
CaseClass("Product", List())
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
// Create file with all types
|
|
578
|
+
val file = ScalaFile(
|
|
579
|
+
packageDecl = PackageDecl("com.example"),
|
|
580
|
+
types = types
|
|
581
|
+
)
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Reusing Type Definitions
|
|
585
|
+
|
|
586
|
+
Define common types once and compose them:
|
|
587
|
+
|
|
588
|
+
```scala
|
|
589
|
+
import zio.blocks.codegen.ir._
|
|
590
|
+
|
|
591
|
+
// Common error type
|
|
592
|
+
val baseError = SealedTrait(
|
|
593
|
+
"Error",
|
|
594
|
+
cases = List(
|
|
595
|
+
SealedTraitCase.CaseObjectCase("NotFound")
|
|
596
|
+
)
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
// File 1
|
|
600
|
+
val file1 = ScalaFile(
|
|
601
|
+
packageDecl = PackageDecl("com.module1"),
|
|
602
|
+
types = List(baseError)
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
// File 2 (same error, different package)
|
|
606
|
+
val file2 = ScalaFile(
|
|
607
|
+
packageDecl = PackageDecl("com.module2"),
|
|
608
|
+
types = List(baseError)
|
|
609
|
+
)
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Configuration Strategies
|
|
613
|
+
|
|
614
|
+
Choose configuration once and apply to all files:
|
|
615
|
+
|
|
616
|
+
```scala
|
|
617
|
+
import zio.blocks.codegen.emit._
|
|
618
|
+
|
|
619
|
+
// Your project's standard config
|
|
620
|
+
val projectConfig = EmitterConfig(
|
|
621
|
+
indentWidth = 2,
|
|
622
|
+
trailingCommas = true,
|
|
623
|
+
sortImports = true,
|
|
624
|
+
scala3Syntax = true
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
// Apply to all generated files
|
|
628
|
+
// ScalaEmitter.emit(file1, projectConfig)
|
|
629
|
+
// ScalaEmitter.emit(file2, projectConfig)
|
|
630
|
+
// ScalaEmitter.emit(file3, projectConfig)
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
## Integration with Generators
|
|
634
|
+
|
|
635
|
+
In a real code generator, you'd:
|
|
636
|
+
|
|
637
|
+
1. **Parse** your source format (OpenAPI, Smithy, Protobuf, etc.)
|
|
638
|
+
2. **Build IR** models from parsed data
|
|
639
|
+
3. **Configure** the emitter once
|
|
640
|
+
4. **Emit** files in a loop
|
|
641
|
+
5. **Write** strings to disk
|
|
642
|
+
|
|
643
|
+
Example structure:
|
|
644
|
+
|
|
645
|
+
```scala
|
|
646
|
+
// Pseudocode: real generators follow this pattern
|
|
647
|
+
|
|
648
|
+
// def generateFromOpenAPI(spec: OpenAPI): Unit = {
|
|
649
|
+
// val config = EmitterConfig(...)
|
|
650
|
+
//
|
|
651
|
+
// for (schema <- spec.schemas) {
|
|
652
|
+
// val irType = openAPISchemaToIR(schema) // Parse → IR
|
|
653
|
+
// val file = ScalaFile(
|
|
654
|
+
// packageDecl = PackageDecl("com.generated"),
|
|
655
|
+
// types = List(irType)
|
|
656
|
+
// )
|
|
657
|
+
// val code = ScalaEmitter.emit(file, config) // IR → Scala
|
|
658
|
+
// writeFile(s"generated/${schema.name}.scala", code) // Write
|
|
659
|
+
// }
|
|
660
|
+
// }
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
This separation of concerns (format-specific parsing → generic IR → emission) is the core value of `zio-blocks-codegen`.
|
|
664
|
+
|