@zio.dev/zio-blocks 0.0.21 → 0.0.22
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/index.md +49 -29
- package/package.json +1 -1
- package/path-interpolator.md +24 -23
- package/reference/codec.md +384 -0
- package/reference/docs.md +1 -1
- package/reference/dynamic-optic.md +392 -0
- package/reference/formats.md +68 -12
- package/reference/json-schema.md +13 -10
- package/reference/lazy.md +361 -0
- package/reference/modifier.md +340 -0
- package/reference/syntax.md +11 -11
- package/reference/type-class-derivation.md +1959 -0
- package/scope.md +230 -232
- package/sidebars.js +6 -1
package/index.md
CHANGED
|
@@ -80,14 +80,14 @@ val thriftCodec = Schema[Person].derive(ThriftFormat) // Thrift
|
|
|
80
80
|
### Installation
|
|
81
81
|
|
|
82
82
|
```scala
|
|
83
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
83
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.22"
|
|
84
84
|
|
|
85
85
|
// Optional format modules:
|
|
86
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-avro" % "0.0.
|
|
87
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.
|
|
88
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-messagepack" % "0.0.
|
|
89
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.
|
|
90
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-bson" % "0.0.
|
|
86
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-avro" % "0.0.22"
|
|
87
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.22"
|
|
88
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-messagepack" % "0.0.22"
|
|
89
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.22"
|
|
90
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-bson" % "0.0.22"
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
### Example: Optics
|
|
@@ -142,7 +142,7 @@ Chunk is designed for:
|
|
|
142
142
|
### Installation
|
|
143
143
|
|
|
144
144
|
```scala
|
|
145
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-chunk" % "0.0.
|
|
145
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-chunk" % "0.0.22"
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
### Example
|
|
@@ -173,7 +173,7 @@ val head: Int = nonEmpty.head // Always safe, no Option needed
|
|
|
173
173
|
|
|
174
174
|
## Scope
|
|
175
175
|
|
|
176
|
-
Compile-time verified resource safety for synchronous Scala code. Scope prevents resource leaks at compile time by tagging values with an unnameable type-level identity—values allocated in a scope can only be used within that scope
|
|
176
|
+
Compile-time verified resource safety for synchronous Scala code. Scope prevents resource leaks at compile time by tagging values with an unnameable type-level identity—values allocated in a scope can only be used within that scope. Child scope values cannot escape to parent scopes, enforced by both the abstract scope-tagged type and the `Unscoped` constraint on `scoped`.
|
|
177
177
|
|
|
178
178
|
### The Problem
|
|
179
179
|
|
|
@@ -204,11 +204,13 @@ Scope makes resource leaks a **compile error**, not a runtime bug:
|
|
|
204
204
|
import zio.blocks.scope._
|
|
205
205
|
|
|
206
206
|
Scope.global.scoped { scope =>
|
|
207
|
-
|
|
207
|
+
import scope._
|
|
208
|
+
|
|
209
|
+
val db: $[Database] = allocate(Resource(openDatabase()))
|
|
208
210
|
|
|
209
211
|
// Methods are hidden - can't call db.query() directly
|
|
210
|
-
// Must use scope
|
|
211
|
-
val result = (
|
|
212
|
+
// Must use scope.use to access:
|
|
213
|
+
val result = scope.use(db)(_.query("SELECT 1"))
|
|
212
214
|
|
|
213
215
|
// Trying to return `db` would be a compile error!
|
|
214
216
|
result // Only pure data escapes
|
|
@@ -218,16 +220,17 @@ Scope.global.scoped { scope =>
|
|
|
218
220
|
|
|
219
221
|
### Key Features
|
|
220
222
|
|
|
221
|
-
- **Compile-Time Leak Prevention**: Values
|
|
222
|
-
- **Zero Runtime Overhead**:
|
|
223
|
+
- **Compile-Time Leak Prevention**: Values of type `scope.$[A]` are opaque and unique to each scope instance. Returning a scoped value from its scope is a type error.
|
|
224
|
+
- **Zero Runtime Overhead**: `$[A]` erases to `A` at runtime—zero allocation overhead.
|
|
223
225
|
- **Structured Scopes**: Child scopes nest within parents; resources clean up LIFO when scopes exit.
|
|
224
226
|
- **Built-in Dependency Injection**: Wire up your application with `Resource.from[T](wires*)` for automatic constructor-based DI.
|
|
225
227
|
- **AutoCloseable Integration**: Resources implementing `AutoCloseable` have `close()` registered automatically.
|
|
228
|
+
- **Unscoped Constraint**: The `scoped` method requires `Unscoped[A]` evidence on the return type, ensuring only pure data (not resources or closures) can escape.
|
|
226
229
|
|
|
227
230
|
### Installation
|
|
228
231
|
|
|
229
232
|
```scala
|
|
230
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-scope" % "0.0.
|
|
233
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-scope" % "0.0.22"
|
|
231
234
|
```
|
|
232
235
|
|
|
233
236
|
### Example: Basic Resource Management
|
|
@@ -241,11 +244,14 @@ final class Database extends AutoCloseable {
|
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
Scope.global.scoped { scope =>
|
|
244
|
-
|
|
245
|
-
|
|
247
|
+
import scope._
|
|
248
|
+
|
|
249
|
+
// Allocate returns scope.$[Database] (scoped value)
|
|
250
|
+
val db: $[Database] = allocate(Resource(new Database))
|
|
251
|
+
|
|
252
|
+
// Access via scope.use - result (String) escapes, db does not
|
|
253
|
+
val result = scope.use(db)(_.query("SELECT * FROM users"))
|
|
246
254
|
|
|
247
|
-
// Access via scope $ - result (String) escapes, db does not
|
|
248
|
-
val result = (scope $ db)(_.query("SELECT * FROM users"))
|
|
249
255
|
println(result)
|
|
250
256
|
}
|
|
251
257
|
// Output: Result: SELECT * FROM users
|
|
@@ -269,8 +275,11 @@ val serviceResource: Resource[UserService] = Resource.from[UserService](
|
|
|
269
275
|
)
|
|
270
276
|
|
|
271
277
|
Scope.global.scoped { scope =>
|
|
272
|
-
|
|
273
|
-
|
|
278
|
+
import scope._
|
|
279
|
+
|
|
280
|
+
val service = allocate(serviceResource)
|
|
281
|
+
|
|
282
|
+
scope.use(service)(_.createUser("Alice"))
|
|
274
283
|
}
|
|
275
284
|
// Cleanup runs LIFO: UserService → Database (UserRepo has no cleanup)
|
|
276
285
|
```
|
|
@@ -279,14 +288,24 @@ Scope.global.scoped { scope =>
|
|
|
279
288
|
|
|
280
289
|
```scala
|
|
281
290
|
Scope.global.scoped { connScope =>
|
|
282
|
-
|
|
291
|
+
import connScope._
|
|
292
|
+
|
|
293
|
+
val conn = allocate(Resource.fromAutoCloseable(new Connection))
|
|
283
294
|
|
|
284
295
|
// Transaction lives in child scope - cleaned up before connection
|
|
285
|
-
val result =
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
296
|
+
val result: String = scoped { txScope =>
|
|
297
|
+
import txScope._
|
|
298
|
+
|
|
299
|
+
// For-comprehension: flatMap unwraps $[Connection] to Connection,
|
|
300
|
+
// so c.beginTransaction() returns a raw Resource[Transaction]
|
|
301
|
+
for {
|
|
302
|
+
c <- lower(conn)
|
|
303
|
+
tx <- allocate(c.beginTransaction())
|
|
304
|
+
} yield {
|
|
305
|
+
tx.execute("INSERT INTO users VALUES (1, 'Alice')")
|
|
306
|
+
tx.commit()
|
|
307
|
+
"success"
|
|
308
|
+
}
|
|
290
309
|
}
|
|
291
310
|
// Transaction closed here, connection still open
|
|
292
311
|
|
|
@@ -321,7 +340,7 @@ Generating documentation, README files, or any Markdown content programmatically
|
|
|
321
340
|
### Installation
|
|
322
341
|
|
|
323
342
|
```scala
|
|
324
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-docs" % "0.0.
|
|
343
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-docs" % "0.0.22"
|
|
325
344
|
```
|
|
326
345
|
|
|
327
346
|
### Example
|
|
@@ -405,7 +424,7 @@ Compile-time type identity with rich metadata. TypeId captures comprehensive inf
|
|
|
405
424
|
### Installation
|
|
406
425
|
|
|
407
426
|
```scala
|
|
408
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-typeid" % "0.0.
|
|
427
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-typeid" % "0.0.22"
|
|
409
428
|
```
|
|
410
429
|
|
|
411
430
|
### Example
|
|
@@ -448,7 +467,7 @@ A type-indexed heterogeneous collection that stores values by their types with c
|
|
|
448
467
|
### Installation
|
|
449
468
|
|
|
450
469
|
```scala
|
|
451
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-context" % "0.0.
|
|
470
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-context" % "0.0.22"
|
|
452
471
|
```
|
|
453
472
|
|
|
454
473
|
### Example
|
|
@@ -534,6 +553,7 @@ ZIO Blocks supports **Scala 2.13** and **Scala 3.x** with full source compatibil
|
|
|
534
553
|
|
|
535
554
|
### Serialization
|
|
536
555
|
|
|
556
|
+
- [Codec & Format](./reference/codec.md) - Codec, Format, BinaryCodec & TextCodec
|
|
537
557
|
- [JSON](./reference/json.md) - JSON codec and parsing
|
|
538
558
|
- [JSON Schema](./reference/json-schema.md) - JSON Schema generation and validation
|
|
539
559
|
- [Formats](./reference/formats.md) - Avro, TOON, MessagePack, BSON, Thrift
|
package/package.json
CHANGED
package/path-interpolator.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
---
|
|
2
|
+
id: path-interpolator
|
|
3
|
+
title: "Path Interpolator"
|
|
4
|
+
---
|
|
4
5
|
|
|
5
6
|
The path interpolator `p"..."` is a compile-time string interpolator for constructing `DynamicOptic` instances in ZIO Blocks. It provides a clean, concise syntax for building optic paths that navigate through complex data structures, with all parsing and validation happening at compile time for zero runtime overhead.
|
|
6
7
|
|
|
@@ -215,14 +216,14 @@ p"<A><B><C>" // Nested variants
|
|
|
215
216
|
|
|
216
217
|
String and character literals support standard escape sequences:
|
|
217
218
|
|
|
218
|
-
| Escape | Result
|
|
219
|
-
|
|
220
|
-
| `\n` | newline | Line feed
|
|
221
|
-
| `\t` | tab
|
|
222
|
-
| `\r` | return
|
|
223
|
-
| `\'` | `'`
|
|
224
|
-
| `\"` | `"`
|
|
225
|
-
| `\\` | `\`
|
|
219
|
+
| Escape | Result | Description |
|
|
220
|
+
|--------|---------|-----------------|
|
|
221
|
+
| `\n` | newline | Line feed |
|
|
222
|
+
| `\t` | tab | Horizontal tab |
|
|
223
|
+
| `\r` | return | Carriage return |
|
|
224
|
+
| `\'` | `'` | Single quote |
|
|
225
|
+
| `\"` | `"` | Double quote |
|
|
226
|
+
| `\\` | `\` | Backslash |
|
|
226
227
|
|
|
227
228
|
**Examples:**
|
|
228
229
|
|
|
@@ -618,19 +619,19 @@ val same = p".users[*].email"
|
|
|
618
619
|
|
|
619
620
|
**Examples:**
|
|
620
621
|
|
|
621
|
-
| DynamicOptic Construction
|
|
622
|
-
|
|
623
|
-
| `DynamicOptic.root.field("name")`
|
|
622
|
+
| DynamicOptic Construction | toString Output |
|
|
623
|
+
|------------------------------------------------------|-------------------|
|
|
624
|
+
| `DynamicOptic.root.field("name")` | `.name` |
|
|
624
625
|
| `DynamicOptic.root.field("address").field("street")` | `.address.street` |
|
|
625
|
-
| `DynamicOptic.root.caseOf("Some")`
|
|
626
|
-
| `DynamicOptic.root.at(0)`
|
|
627
|
-
| `DynamicOptic.root.atIndices(0, 2, 5)`
|
|
628
|
-
| `DynamicOptic.elements`
|
|
629
|
-
| `DynamicOptic.root.atKey("host")`
|
|
630
|
-
| `DynamicOptic.root.atKey(80)`
|
|
631
|
-
| `DynamicOptic.mapValues`
|
|
632
|
-
| `DynamicOptic.mapKeys`
|
|
633
|
-
| `DynamicOptic.wrapped`
|
|
626
|
+
| `DynamicOptic.root.caseOf("Some")` | `<Some>` |
|
|
627
|
+
| `DynamicOptic.root.at(0)` | `[0]` |
|
|
628
|
+
| `DynamicOptic.root.atIndices(0, 2, 5)` | `[0,2,5]` |
|
|
629
|
+
| `DynamicOptic.elements` | `[*]` |
|
|
630
|
+
| `DynamicOptic.root.atKey("host")` | `{"host"}` |
|
|
631
|
+
| `DynamicOptic.root.atKey(80)` | `{80}` |
|
|
632
|
+
| `DynamicOptic.mapValues` | `{*}` |
|
|
633
|
+
| `DynamicOptic.mapKeys` | `{*:}` |
|
|
634
|
+
| `DynamicOptic.wrapped` | `.~` |
|
|
634
635
|
|
|
635
636
|
## Summary
|
|
636
637
|
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: codec
|
|
3
|
+
title: "Codec"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`Codec[DecodeInput, EncodeOutput, Value]` is the base abstraction for encoding and decoding values between a specific input representation and a specific output representation. It forms the foundation of ZIO Blocks' multi-format serialization system, enabling a single `Schema[A]` to derive codecs for JSON, Avro, TOON, MessagePack, Thrift, and other formats that are integrated via the `Codec`/`Format` system. BSON support is provided separately via `BsonSchemaCodec`, which is not a subtype of `codec.Codec` and is not derived via `Schema.derive(format)`.
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
`Codec` defines two abstract methods that every concrete codec must implement:
|
|
11
|
+
|
|
12
|
+
```scala
|
|
13
|
+
abstract class Codec[DecodeInput, EncodeOutput, Value] {
|
|
14
|
+
def encode(value: Value, output: EncodeOutput): Unit
|
|
15
|
+
def decode(input: DecodeInput): Either[SchemaError, Value]
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **`encode`** writes the encoded form of `value` into `output`. The output parameter is typically a mutable buffer (`ByteBuffer`, `CharBuffer`) that the caller provides.
|
|
20
|
+
- **`decode`** reads from `input` and returns either a `SchemaError` describing the failure or the decoded value.
|
|
21
|
+
|
|
22
|
+
End users rarely interact with `Codec` directly. Instead, they work with format-specific subclasses like `JsonBinaryCodec[A]` or `ToonBinaryCodec[A]`, which add convenience methods for common input/output types.
|
|
23
|
+
|
|
24
|
+
Given a `Schema[A]`, you can derive a codec for any supported format by calling `Schema[A].derive(format)`, which uses the `Deriver` associated with that format to generate the appropriate codec instance. For example, to derive a JSON codec:
|
|
25
|
+
|
|
26
|
+
```scala
|
|
27
|
+
import zio.blocks.schema._
|
|
28
|
+
import zio.blocks.schema.json._
|
|
29
|
+
|
|
30
|
+
case class Person(name: String, age: Int)
|
|
31
|
+
|
|
32
|
+
object Person {
|
|
33
|
+
// Derive a schema for Person (required for codec derivation)
|
|
34
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
35
|
+
// Derive a JSON codec from the schema
|
|
36
|
+
implicit val codec: JsonBinaryCodec[Person] = schema.derive[JsonFormat.type](JsonFormat)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Encode
|
|
40
|
+
val bytes: Array[Byte] = Person.codec.encode(Person("Alice", 30))
|
|
41
|
+
|
|
42
|
+
// Decode
|
|
43
|
+
val result: Either[SchemaError, Person] = Person.codec.decode(bytes)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
To include the base schema module with JSON support, add the following dependency to your `build.sbt`:
|
|
49
|
+
|
|
50
|
+
```scala
|
|
51
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.22"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Additional format modules are separate artifacts:
|
|
55
|
+
|
|
56
|
+
```scala
|
|
57
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-avro" % "0.0.22"
|
|
58
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.22"
|
|
59
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-messagepack" % "0.0.22"
|
|
60
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.22"
|
|
61
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-bson" % "0.0.22"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For cross-platform projects (Scala.js):
|
|
65
|
+
|
|
66
|
+
```scala
|
|
67
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.22"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Supported Scala versions: 2.13.x and 3.x.
|
|
71
|
+
|
|
72
|
+
## BinaryCodec and TextCodec
|
|
73
|
+
|
|
74
|
+
The codec system in ZIO Blocks is organized as a layered hierarchy:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Codec[DecodeInput, EncodeOutput, Value]
|
|
78
|
+
├── BinaryCodec[A] = Codec[ByteBuffer, ByteBuffer, A] (ByteBuffer ↔ A)
|
|
79
|
+
│ ├── JsonBinaryCodec[A]
|
|
80
|
+
│ ├── AvroBinaryCodec[A]
|
|
81
|
+
│ ├── ToonBinaryCodec[A]
|
|
82
|
+
│ ├── ThriftBinaryCodec[A]
|
|
83
|
+
│ └── MessagePackBinaryCodec[A]
|
|
84
|
+
└── TextCodec[A] = Codec[CharBuffer, CharBuffer, A] (CharBuffer ↔ A)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
1. **`BinaryCodec[A]`** fixes both the input and output to `ByteBuffer` and is the base class for all codecs that operate on binary data:
|
|
88
|
+
|
|
89
|
+
```scala
|
|
90
|
+
abstract class BinaryCodec[A] extends Codec[ByteBuffer, ByteBuffer, A]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
2. **`TextCodec[A]`** fixes both the input and output to `CharBuffer`:
|
|
94
|
+
|
|
95
|
+
```scala
|
|
96
|
+
abstract class TextCodec[A] extends Codec[CharBuffer, CharBuffer, A]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
All built-in serialization formats (JSON, Avro, TOON, MessagePack, Thrift) extend `BinaryCodec`. Despite JSON being a text format, the JSON codec operates on UTF-8 encoded bytes for performance.
|
|
100
|
+
|
|
101
|
+
`TextCodec` exists for formats that operate on character data rather than raw bytes. No built-in formats currently use `TextCodec`, but it is available for custom text-based formats.
|
|
102
|
+
|
|
103
|
+
## Deriving Codecs
|
|
104
|
+
|
|
105
|
+
### Using Schema.derive
|
|
106
|
+
|
|
107
|
+
The primary way to obtain a codec is through `Schema[A].derive`:
|
|
108
|
+
|
|
109
|
+
```scala
|
|
110
|
+
import zio.blocks.schema._
|
|
111
|
+
import zio.blocks.schema.json._
|
|
112
|
+
|
|
113
|
+
case class Person(name: String, age: Int)
|
|
114
|
+
object Person {
|
|
115
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Pass a Format object to get a codec for that format
|
|
119
|
+
val jsonCodec: JsonBinaryCodec[Person] = Schema[Person].derive[JsonFormat.type](JsonFormat)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This works with any format:
|
|
123
|
+
|
|
124
|
+
```scala
|
|
125
|
+
import zio.blocks.schema._
|
|
126
|
+
import zio.blocks.schema.json._
|
|
127
|
+
import zio.blocks.schema.toon._
|
|
128
|
+
|
|
129
|
+
case class Person(name: String, age: Int)
|
|
130
|
+
object Person {
|
|
131
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
val jsonCodec = Schema[Person].derive(JsonFormat)
|
|
135
|
+
val toonCodec = Schema[Person].derive(ToonFormat)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Using Schema.deriving for Customization
|
|
139
|
+
|
|
140
|
+
For more control over the derived codec, use `deriving` to get a `DerivationBuilder`. This lets you override instances for specific substructures or inject modifiers before finalizing:
|
|
141
|
+
|
|
142
|
+
```scala
|
|
143
|
+
import zio.blocks.schema._
|
|
144
|
+
import zio.blocks.schema.json._
|
|
145
|
+
|
|
146
|
+
case class Person(name: String, age: Int)
|
|
147
|
+
object Person extends CompanionOptics[Person] {
|
|
148
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
149
|
+
val name = $(_.name)
|
|
150
|
+
val age = $(_.age)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Override the codec for the "name" field
|
|
154
|
+
val customNameCodec = new JsonBinaryCodec[String] {
|
|
155
|
+
def decodeValue(in: JsonReader, default: String): String = in.readString(default)
|
|
156
|
+
def encodeValue(x: String, out: JsonWriter): Unit = out.writeVal(x.toUpperCase)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
val codec: JsonBinaryCodec[Person] = Schema[Person]
|
|
160
|
+
.deriving(JsonFormat.deriver)
|
|
161
|
+
.instance(Person.name, customNameCodec)
|
|
162
|
+
.derive
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Using Schema#decode and Schema#encode
|
|
166
|
+
|
|
167
|
+
`Schema` also provides `decode` and `encode` methods that internally call `derive` (with caching) and then delegate to the codec:
|
|
168
|
+
|
|
169
|
+
```scala
|
|
170
|
+
import zio.blocks.schema._
|
|
171
|
+
import zio.blocks.schema.json._
|
|
172
|
+
import java.nio.ByteBuffer
|
|
173
|
+
|
|
174
|
+
case class Person(name: String, age: Int)
|
|
175
|
+
object Person {
|
|
176
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Encode directly from Schema
|
|
180
|
+
val buffer = ByteBuffer.allocate(1024)
|
|
181
|
+
Schema[Person].encode(JsonFormat)(buffer)(Person("Alice", 30))
|
|
182
|
+
|
|
183
|
+
// Decode directly from Schema
|
|
184
|
+
buffer.flip()
|
|
185
|
+
val result: Either[SchemaError, Person] = Schema[Person].decode(JsonFormat)(buffer)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Using a Deriver Directly
|
|
189
|
+
|
|
190
|
+
Each `Format` object contains a `Deriver[TC]` that can also be passed to `derive`:
|
|
191
|
+
|
|
192
|
+
```scala
|
|
193
|
+
import zio.blocks.schema._
|
|
194
|
+
import zio.blocks.schema.json._
|
|
195
|
+
|
|
196
|
+
case class Person(name: String, age: Int)
|
|
197
|
+
object Person {
|
|
198
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// These are equivalent:
|
|
202
|
+
val codec1 = Schema[Person].derive(JsonFormat)
|
|
203
|
+
val codec2 = Schema[Person].derive(JsonFormat.deriver)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Passing a `Deriver` directly is useful when working with custom or configured derivers (see [Configuring Codecs](#configuring-codecs)).
|
|
207
|
+
|
|
208
|
+
## Convenience Methods on Format-Specific Codecs
|
|
209
|
+
|
|
210
|
+
While the base `Codec` class defines only `encode(value, output)` and `decode(input)`, format-specific subclasses like `JsonBinaryCodec` and `ToonBinaryCodec` add convenience overloads for common I/O types.
|
|
211
|
+
|
|
212
|
+
### JsonBinaryCodec Convenience Methods
|
|
213
|
+
|
|
214
|
+
`JsonBinaryCodec[A]` provides the following overloads beyond the base `ByteBuffer` API:
|
|
215
|
+
|
|
216
|
+
```scala
|
|
217
|
+
import zio.blocks.schema._
|
|
218
|
+
import zio.blocks.schema.json._
|
|
219
|
+
|
|
220
|
+
case class Person(name: String, age: Int)
|
|
221
|
+
object Person {
|
|
222
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
val codec = Schema[Person].derive(JsonFormat)
|
|
226
|
+
val person = Person("Alice", 30)
|
|
227
|
+
|
|
228
|
+
// Array[Byte]
|
|
229
|
+
val bytes: Array[Byte] = codec.encode(person)
|
|
230
|
+
val fromBytes: Either[SchemaError, Person] = codec.decode(bytes)
|
|
231
|
+
|
|
232
|
+
// String
|
|
233
|
+
val jsonStr: String = codec.encodeToString(person)
|
|
234
|
+
val fromStr: Either[SchemaError, Person] = codec.decode("""{"name":"Alice","age":30}""")
|
|
235
|
+
|
|
236
|
+
// InputStream / OutputStream
|
|
237
|
+
import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
|
|
238
|
+
|
|
239
|
+
val os = new ByteArrayOutputStream()
|
|
240
|
+
codec.encode(person, os)
|
|
241
|
+
|
|
242
|
+
val is = new ByteArrayInputStream(os.toByteArray)
|
|
243
|
+
val fromStream: Either[SchemaError, Person] = codec.decode(is)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### ToonBinaryCodec Convenience Methods
|
|
247
|
+
|
|
248
|
+
`ToonBinaryCodec[A]` provides the same set of overloads:
|
|
249
|
+
|
|
250
|
+
```scala
|
|
251
|
+
import zio.blocks.schema._
|
|
252
|
+
import zio.blocks.schema.toon._
|
|
253
|
+
|
|
254
|
+
case class Person(name: String, age: Int)
|
|
255
|
+
object Person {
|
|
256
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
val codec = Schema[Person].derive(ToonFormat)
|
|
260
|
+
val person = Person("Alice", 30)
|
|
261
|
+
|
|
262
|
+
// Array[Byte]
|
|
263
|
+
val bytes: Array[Byte] = codec.encode(person)
|
|
264
|
+
val fromBytes: Either[SchemaError, Person] = codec.decode(bytes)
|
|
265
|
+
|
|
266
|
+
// String
|
|
267
|
+
val toonStr: String = codec.encodeToString(person)
|
|
268
|
+
val fromStr: Either[SchemaError, Person] = codec.decode("name: Alice\nage: 30")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Summary of Convenience Methods
|
|
272
|
+
|
|
273
|
+
`BinaryCodec` subclasses (JSON, TOON, MessagePack, Avro, Thrift) expose the following convenience overloads (availability may vary by format):
|
|
274
|
+
|
|
275
|
+
| Method | Description |
|
|
276
|
+
|------------------------------------------------------|----------------------------------------------|
|
|
277
|
+
| `encode(value): Array[Byte]` | Encode to a byte array |
|
|
278
|
+
| `decode(input: Array[Byte]): Either[SchemaError, A]` | Decode from a byte array |
|
|
279
|
+
| `encode(value, output: ByteBuffer): Unit` | Encode into a `ByteBuffer` |
|
|
280
|
+
| `decode(input: ByteBuffer): Either[SchemaError, A]` | Decode from a `ByteBuffer` |
|
|
281
|
+
| `encode(value, output: OutputStream): Unit` | Encode into an `OutputStream` (JSON, TOON, Avro) |
|
|
282
|
+
| `decode(input: InputStream): Either[SchemaError, A]` | Decode from an `InputStream` (JSON, TOON, Avro) |
|
|
283
|
+
| `encodeToString(value): String` | Encode to a `String` (JSON, TOON) |
|
|
284
|
+
| `decode(input: String): Either[SchemaError, A]` | Decode from a `String` (JSON, TOON) |
|
|
285
|
+
|
|
286
|
+
The `String`-based methods are available on text-oriented binary codecs (JSON, TOON) but not on purely binary formats like Avro or Thrift.
|
|
287
|
+
|
|
288
|
+
## Configuring Codecs
|
|
289
|
+
|
|
290
|
+
Format-specific derivers support configuration options that control encoding behavior. Instead of passing a `Format` object to `derive`, you pass a configured `Deriver`:
|
|
291
|
+
|
|
292
|
+
### JSON Configuration
|
|
293
|
+
|
|
294
|
+
```scala
|
|
295
|
+
import zio.blocks.schema._
|
|
296
|
+
import zio.blocks.schema.json._
|
|
297
|
+
|
|
298
|
+
case class Person(
|
|
299
|
+
firstName: String,
|
|
300
|
+
lastName: String,
|
|
301
|
+
middleName: Option[String] = None
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
object Person {
|
|
305
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
val customDeriver = JsonBinaryCodecDeriver
|
|
309
|
+
.withFieldNameMapper(NameMapper.SnakeCase)
|
|
310
|
+
.withTransientNone(true)
|
|
311
|
+
.withRejectExtraFields(true)
|
|
312
|
+
|
|
313
|
+
val codec = Schema[Person].derive(customDeriver)
|
|
314
|
+
|
|
315
|
+
// Encodes as: {"first_name":"Alice","last_name":"Smith"}
|
|
316
|
+
// (middleName omitted because it is None and transientNone is true)
|
|
317
|
+
val json = codec.encodeToString(Person("Alice", "Smith"))
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
| Option | Description | Default |
|
|
321
|
+
|---------------------------------|--------------------------------------------------------|------------|
|
|
322
|
+
| `withFieldNameMapper` | Transform field names (Identity, SnakeCase, KebabCase) | `Identity` |
|
|
323
|
+
| `withCaseNameMapper` | Transform variant/case names | `Identity` |
|
|
324
|
+
| `withDiscriminatorKind` | ADT discriminator style (Key, Field, None) | `Key` |
|
|
325
|
+
| `withRejectExtraFields` | Error on unknown fields during decoding | `false` |
|
|
326
|
+
| `withEnumValuesAsStrings` | Encode enum values as strings | `true` |
|
|
327
|
+
| `withTransientNone` | Omit `None` values from output | `true` |
|
|
328
|
+
| `withTransientEmptyCollection` | Omit empty collections from output | `true` |
|
|
329
|
+
| `withTransientDefaultValue` | Omit fields with default values | `true` |
|
|
330
|
+
| `withRequireOptionFields` | Require optional fields in input | `false` |
|
|
331
|
+
| `withRequireCollectionFields` | Require collection fields in input | `false` |
|
|
332
|
+
| `withRequireDefaultValueFields` | Require fields with defaults in input | `false` |
|
|
333
|
+
|
|
334
|
+
### TOON Configuration
|
|
335
|
+
|
|
336
|
+
```scala
|
|
337
|
+
import zio.blocks.schema._
|
|
338
|
+
import zio.blocks.schema.toon._
|
|
339
|
+
|
|
340
|
+
case class Person(
|
|
341
|
+
firstName: String,
|
|
342
|
+
lastName: String
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
object Person {
|
|
346
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
val customDeriver = ToonBinaryCodecDeriver
|
|
350
|
+
.withFieldNameMapper(NameMapper.SnakeCase)
|
|
351
|
+
.withArrayFormat(ArrayFormat.Tabular)
|
|
352
|
+
.withDiscriminatorKind(DiscriminatorKind.Field("type"))
|
|
353
|
+
|
|
354
|
+
val codec = Schema[Person].derive(customDeriver)
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Error Handling
|
|
358
|
+
|
|
359
|
+
All `decode` operations return `Either[SchemaError, A]`. `SchemaError` includes path information that pinpoints where in the data structure decoding failed:
|
|
360
|
+
|
|
361
|
+
```scala
|
|
362
|
+
import zio.blocks.schema._
|
|
363
|
+
import zio.blocks.schema.json._
|
|
364
|
+
|
|
365
|
+
case class Address(street: String, city: String)
|
|
366
|
+
case class Person(name: String, address: Address)
|
|
367
|
+
|
|
368
|
+
object Address {
|
|
369
|
+
implicit val schema: Schema[Address] = Schema.derived
|
|
370
|
+
}
|
|
371
|
+
object Person {
|
|
372
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
val codec = Schema[Person].derive(JsonFormat)
|
|
376
|
+
|
|
377
|
+
// Missing required field
|
|
378
|
+
val result = codec.decode("""{"name":"Alice","address":{}}""")
|
|
379
|
+
|
|
380
|
+
result match {
|
|
381
|
+
case Right(person) => println(person)
|
|
382
|
+
case Left(error) => error.errors.foreach(e => println(s"Error: ${e.message}"))
|
|
383
|
+
}
|
|
384
|
+
```
|
package/reference/docs.md
CHANGED
|
@@ -10,7 +10,7 @@ Complete API reference for the zio-blocks-docs module - a zero-dependency GitHub
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
12
12
|
```scala
|
|
13
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-docs" % "0.0.
|
|
13
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-docs" % "0.0.22"
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Core Types
|