@zio.dev/zio-blocks 0.0.1
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 +426 -0
- package/package.json +6 -0
- package/path-interpolator.md +645 -0
- package/reference/binding.md +364 -0
- package/reference/chunk.md +576 -0
- package/reference/context.md +157 -0
- package/reference/docs.md +524 -0
- package/reference/dynamic-value.md +823 -0
- package/reference/formats.md +640 -0
- package/reference/json-schema.md +626 -0
- package/reference/json.md +979 -0
- package/reference/modifier.md +276 -0
- package/reference/optics.md +1613 -0
- package/reference/patch.md +631 -0
- package/reference/reflect-transform.md +387 -0
- package/reference/reflect.md +521 -0
- package/reference/registers.md +282 -0
- package/reference/schema-evolution.md +540 -0
- package/reference/schema.md +619 -0
- package/reference/syntax.md +409 -0
- package/reference/type-class-derivation-internals.md +632 -0
- package/reference/typeid.md +900 -0
- package/reference/validation.md +458 -0
- package/scope.md +627 -0
- package/sidebars.js +30 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: syntax
|
|
3
|
+
title: "Extension Syntax"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
ZIO Blocks provides convenient extension methods on any value that has a `Schema`. These methods give you fluent, type-safe access to JSON encoding/decoding, pretty-printing, and patching operations directly on your values.
|
|
7
|
+
|
|
8
|
+
## Import
|
|
9
|
+
|
|
10
|
+
To use the extension syntax, import the schema package:
|
|
11
|
+
|
|
12
|
+
```scala mdoc:compile-only
|
|
13
|
+
import zio.blocks.schema._
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This brings the extension methods into scope for any type with an implicit `Schema` instance.
|
|
17
|
+
|
|
18
|
+
## Quick Example
|
|
19
|
+
|
|
20
|
+
```scala mdoc:compile-only
|
|
21
|
+
import zio.blocks.schema._
|
|
22
|
+
|
|
23
|
+
case class Person(name: String, age: Int)
|
|
24
|
+
object Person {
|
|
25
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
val alice = Person("Alice", 30)
|
|
29
|
+
|
|
30
|
+
// Convert to JSON
|
|
31
|
+
val json = alice.toJson // Json AST
|
|
32
|
+
val jsonStr = alice.toJsonString // {"name":"Alice","age":30}
|
|
33
|
+
val jsonBytes = alice.toJsonBytes // Array[Byte]
|
|
34
|
+
|
|
35
|
+
// Parse from JSON
|
|
36
|
+
val parsed = """{"name":"Bob","age":25}""".fromJson[Person]
|
|
37
|
+
// Right(Person("Bob", 25))
|
|
38
|
+
|
|
39
|
+
// Pretty-print
|
|
40
|
+
val shown = alice.show // Record { name = Alice, age = 30 }
|
|
41
|
+
|
|
42
|
+
// Compute and apply patches
|
|
43
|
+
val bob = Person("Bob", 30)
|
|
44
|
+
val patch = alice.diff(bob) // Patch that changes name
|
|
45
|
+
val result = alice.applyPatch(patch) // Person("Bob", 30)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## JSON Encoding Methods
|
|
49
|
+
|
|
50
|
+
### toJson
|
|
51
|
+
|
|
52
|
+
Converts a value to a `Json` AST (abstract syntax tree):
|
|
53
|
+
|
|
54
|
+
```scala mdoc:compile-only
|
|
55
|
+
import zio.blocks.schema._
|
|
56
|
+
|
|
57
|
+
case class Point(x: Int, y: Int)
|
|
58
|
+
object Point {
|
|
59
|
+
implicit val schema: Schema[Point] = Schema.derived
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
val point = Point(10, 20)
|
|
63
|
+
val json = point.toJson
|
|
64
|
+
// Json.Object(Vector("x" -> Json.Number(10), "y" -> Json.Number(20)))
|
|
65
|
+
|
|
66
|
+
// Navigate and extract values
|
|
67
|
+
json.get("x").as[Int] // Right(10)
|
|
68
|
+
json.get("y").as[Int] // Right(20)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### toJsonString
|
|
72
|
+
|
|
73
|
+
Converts a value directly to a JSON string:
|
|
74
|
+
|
|
75
|
+
```scala mdoc:compile-only
|
|
76
|
+
import zio.blocks.schema._
|
|
77
|
+
|
|
78
|
+
case class User(name: String, email: String)
|
|
79
|
+
object User {
|
|
80
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
val user = User("Alice", "alice@example.com")
|
|
84
|
+
val jsonStr = user.toJsonString
|
|
85
|
+
// {"name":"Alice","email":"alice@example.com"}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### toJsonBytes
|
|
89
|
+
|
|
90
|
+
Converts a value to a UTF-8 encoded byte array. This is useful for efficient serialization when working with binary protocols or network I/O:
|
|
91
|
+
|
|
92
|
+
```scala mdoc:compile-only
|
|
93
|
+
import zio.blocks.schema._
|
|
94
|
+
|
|
95
|
+
case class Message(id: Long, content: String)
|
|
96
|
+
object Message {
|
|
97
|
+
implicit val schema: Schema[Message] = Schema.derived
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
val msg = Message(42, "Hello, world!")
|
|
101
|
+
val bytes: Array[Byte] = msg.toJsonBytes
|
|
102
|
+
|
|
103
|
+
// Useful for sending over the wire
|
|
104
|
+
// socket.write(bytes)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## JSON Decoding Methods
|
|
108
|
+
|
|
109
|
+
### fromJson (on String)
|
|
110
|
+
|
|
111
|
+
Parses a JSON string into a typed value:
|
|
112
|
+
|
|
113
|
+
```scala mdoc:compile-only
|
|
114
|
+
import zio.blocks.schema._
|
|
115
|
+
|
|
116
|
+
case class Config(host: String, port: Int)
|
|
117
|
+
object Config {
|
|
118
|
+
implicit val schema: Schema[Config] = Schema.derived
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
val jsonStr = """{"host":"localhost","port":8080}"""
|
|
122
|
+
val result: Either[SchemaError, Config] = jsonStr.fromJson[Config]
|
|
123
|
+
// Right(Config("localhost", 8080))
|
|
124
|
+
|
|
125
|
+
// Handle parsing errors
|
|
126
|
+
val invalid = """{"host":"localhost"}""" // missing port
|
|
127
|
+
val error = invalid.fromJson[Config]
|
|
128
|
+
// Left(SchemaError(...))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### fromJson (on Array[Byte])
|
|
132
|
+
|
|
133
|
+
Parses a UTF-8 byte array into a typed value:
|
|
134
|
+
|
|
135
|
+
```scala mdoc:compile-only
|
|
136
|
+
import zio.blocks.schema._
|
|
137
|
+
import java.nio.charset.StandardCharsets
|
|
138
|
+
|
|
139
|
+
case class Event(name: String, timestamp: Long)
|
|
140
|
+
object Event {
|
|
141
|
+
implicit val schema: Schema[Event] = Schema.derived
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
val jsonBytes = """{"name":"click","timestamp":1234567890}"""
|
|
145
|
+
.getBytes(StandardCharsets.UTF_8)
|
|
146
|
+
|
|
147
|
+
val result: Either[SchemaError, Event] = jsonBytes.fromJson[Event]
|
|
148
|
+
// Right(Event("click", 1234567890))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Pretty-Printing
|
|
152
|
+
|
|
153
|
+
### show
|
|
154
|
+
|
|
155
|
+
Converts a value to a human-readable string representation using `DynamicValue`:
|
|
156
|
+
|
|
157
|
+
```scala mdoc:compile-only
|
|
158
|
+
import zio.blocks.schema._
|
|
159
|
+
|
|
160
|
+
case class Address(street: String, city: String, zip: String)
|
|
161
|
+
object Address {
|
|
162
|
+
implicit val schema: Schema[Address] = Schema.derived
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
val addr = Address("123 Main St", "Springfield", "12345")
|
|
166
|
+
val shown = addr.show
|
|
167
|
+
// Record { street = 123 Main St, city = Springfield, zip = 12345 }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
This is useful for debugging and logging, as it provides a consistent, schema-aware representation of any value.
|
|
171
|
+
|
|
172
|
+
## Patching Operations
|
|
173
|
+
|
|
174
|
+
ZIO Blocks includes a powerful patching system for computing and applying differences between values.
|
|
175
|
+
|
|
176
|
+
### diff
|
|
177
|
+
|
|
178
|
+
Computes the difference between two values, returning a `Patch`:
|
|
179
|
+
|
|
180
|
+
```scala mdoc:compile-only
|
|
181
|
+
import zio.blocks.schema._
|
|
182
|
+
|
|
183
|
+
case class Product(name: String, price: Double, stock: Int)
|
|
184
|
+
object Product {
|
|
185
|
+
implicit val schema: Schema[Product] = Schema.derived
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
val before = Product("Widget", 9.99, 100)
|
|
189
|
+
val after = Product("Widget", 12.99, 95)
|
|
190
|
+
|
|
191
|
+
val patch = before.diff(after)
|
|
192
|
+
// Patch contains: price changed from 9.99 to 12.99, stock from 100 to 95
|
|
193
|
+
|
|
194
|
+
patch.isEmpty // false
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
An identical comparison produces an empty patch:
|
|
198
|
+
|
|
199
|
+
```scala mdoc:compile-only
|
|
200
|
+
import zio.blocks.schema._
|
|
201
|
+
|
|
202
|
+
case class Item(id: Int, name: String)
|
|
203
|
+
object Item {
|
|
204
|
+
implicit val schema: Schema[Item] = Schema.derived
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
val item = Item(1, "Example")
|
|
208
|
+
val samePatch = item.diff(item)
|
|
209
|
+
samePatch.isEmpty // true
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### applyPatch
|
|
213
|
+
|
|
214
|
+
Applies a patch to a value, returning the modified value. Uses lenient mode by default, which means operations that can't be applied are silently skipped:
|
|
215
|
+
|
|
216
|
+
```scala mdoc:compile-only
|
|
217
|
+
import zio.blocks.schema._
|
|
218
|
+
|
|
219
|
+
case class Counter(name: String, value: Int)
|
|
220
|
+
object Counter {
|
|
221
|
+
implicit val schema: Schema[Counter] = Schema.derived
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
val counter = Counter("hits", 100)
|
|
225
|
+
val updated = Counter("hits", 150)
|
|
226
|
+
|
|
227
|
+
val patch = counter.diff(updated)
|
|
228
|
+
val result = counter.applyPatch(patch)
|
|
229
|
+
// Counter("hits", 150)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### applyPatchStrict
|
|
233
|
+
|
|
234
|
+
Applies a patch strictly, returning an `Either` that contains an error if any operation fails:
|
|
235
|
+
|
|
236
|
+
```scala mdoc:compile-only
|
|
237
|
+
import zio.blocks.schema._
|
|
238
|
+
import zio.blocks.schema.patch.Patch
|
|
239
|
+
|
|
240
|
+
case class Record(id: String, version: Int)
|
|
241
|
+
object Record {
|
|
242
|
+
implicit val schema: Schema[Record] = Schema.derived
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
val record = Record("abc", 1)
|
|
246
|
+
val newRecord = Record("abc", 2)
|
|
247
|
+
val patch = record.diff(newRecord)
|
|
248
|
+
|
|
249
|
+
val result: Either[SchemaError, Record] = record.applyPatchStrict(patch)
|
|
250
|
+
// Right(Record("abc", 2))
|
|
251
|
+
|
|
252
|
+
// Empty patch also succeeds
|
|
253
|
+
val emptyResult = record.applyPatchStrict(Patch.empty[Record])
|
|
254
|
+
// Right(Record("abc", 1))
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Roundtrip Examples
|
|
258
|
+
|
|
259
|
+
### JSON Roundtrip
|
|
260
|
+
|
|
261
|
+
```scala mdoc:compile-only
|
|
262
|
+
import zio.blocks.schema._
|
|
263
|
+
|
|
264
|
+
case class Order(id: Long, items: List[String], total: BigDecimal)
|
|
265
|
+
object Order {
|
|
266
|
+
implicit val schema: Schema[Order] = Schema.derived
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
val order = Order(12345, List("apple", "banana"), BigDecimal("19.99"))
|
|
270
|
+
|
|
271
|
+
// String roundtrip
|
|
272
|
+
val jsonStr = order.toJsonString
|
|
273
|
+
val decoded1 = jsonStr.fromJson[Order]
|
|
274
|
+
// Right(Order(12345, List("apple", "banana"), 19.99))
|
|
275
|
+
|
|
276
|
+
// Bytes roundtrip
|
|
277
|
+
val jsonBytes = order.toJsonBytes
|
|
278
|
+
val decoded2 = jsonBytes.fromJson[Order]
|
|
279
|
+
// Right(Order(12345, List("apple", "banana"), 19.99))
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Patch Roundtrip
|
|
283
|
+
|
|
284
|
+
```scala mdoc:compile-only
|
|
285
|
+
import zio.blocks.schema._
|
|
286
|
+
|
|
287
|
+
case class Settings(theme: String, fontSize: Int, notifications: Boolean)
|
|
288
|
+
object Settings {
|
|
289
|
+
implicit val schema: Schema[Settings] = Schema.derived
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
val defaults = Settings("light", 14, true)
|
|
293
|
+
val customized = Settings("dark", 16, false)
|
|
294
|
+
|
|
295
|
+
// Compute patch and apply
|
|
296
|
+
val patch = defaults.diff(customized)
|
|
297
|
+
val result = defaults.applyPatch(patch)
|
|
298
|
+
// Settings("dark", 16, false)
|
|
299
|
+
|
|
300
|
+
assert(result == customized)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Edge Cases
|
|
304
|
+
|
|
305
|
+
### Special Characters
|
|
306
|
+
|
|
307
|
+
The JSON encoding handles special characters, Unicode, and escape sequences correctly:
|
|
308
|
+
|
|
309
|
+
```scala mdoc:compile-only
|
|
310
|
+
import zio.blocks.schema._
|
|
311
|
+
|
|
312
|
+
case class Text(content: String)
|
|
313
|
+
object Text {
|
|
314
|
+
implicit val schema: Schema[Text] = Schema.derived
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Special characters
|
|
318
|
+
val special = Text("""John "Jack" O'Brien""")
|
|
319
|
+
val json1 = special.toJsonString
|
|
320
|
+
val decoded1 = json1.fromJson[Text]
|
|
321
|
+
// Right(Text("John \"Jack\" O'Brien"))
|
|
322
|
+
|
|
323
|
+
// Unicode
|
|
324
|
+
val unicode = Text("日本語テキスト")
|
|
325
|
+
val json2 = unicode.toJsonString
|
|
326
|
+
val decoded2 = json2.fromJson[Text]
|
|
327
|
+
// Right(Text("日本語テキスト"))
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Empty and Null Values
|
|
331
|
+
|
|
332
|
+
```scala mdoc:compile-only
|
|
333
|
+
import zio.blocks.schema._
|
|
334
|
+
|
|
335
|
+
case class Profile(name: String, bio: Option[String])
|
|
336
|
+
object Profile {
|
|
337
|
+
implicit val schema: Schema[Profile] = Schema.derived
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Empty strings
|
|
341
|
+
val empty = Profile("", None)
|
|
342
|
+
val json = empty.toJsonString
|
|
343
|
+
val decoded = json.fromJson[Profile]
|
|
344
|
+
// Right(Profile("", None))
|
|
345
|
+
|
|
346
|
+
// Optional fields
|
|
347
|
+
val withBio = Profile("Alice", Some("Developer"))
|
|
348
|
+
val withoutBio = Profile("Bob", None)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Scala 2 vs Scala 3
|
|
352
|
+
|
|
353
|
+
The extension syntax works identically in both Scala 2 and Scala 3, but the implementation differs:
|
|
354
|
+
|
|
355
|
+
### Scala 3 (Extension Methods)
|
|
356
|
+
|
|
357
|
+
```scala
|
|
358
|
+
extension [A](self: A) {
|
|
359
|
+
def toJson(using schema: Schema[A]): Json = ...
|
|
360
|
+
def show(using schema: Schema[A]): String = ...
|
|
361
|
+
def diff(that: A)(using schema: Schema[A]): Patch[A] = ...
|
|
362
|
+
// ...
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
extension (self: String) {
|
|
366
|
+
def fromJson[A](using schema: Schema[A]): Either[SchemaError, A] = ...
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
extension (self: Array[Byte]) {
|
|
370
|
+
def fromJson[A](using schema: Schema[A]): Either[SchemaError, A] = ...
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Scala 2 (Implicit Classes)
|
|
375
|
+
|
|
376
|
+
```scala
|
|
377
|
+
implicit final class SchemaValueOps[A](private val self: A) {
|
|
378
|
+
def toJson(implicit schema: Schema[A]): Json = ...
|
|
379
|
+
def show(implicit schema: Schema[A]): String = ...
|
|
380
|
+
def diff(that: A)(implicit schema: Schema[A]): Patch[A] = ...
|
|
381
|
+
// ...
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
implicit final class StringSchemaOps(private val self: String) {
|
|
385
|
+
def fromJson[A](implicit schema: Schema[A]): Either[SchemaError, A] = ...
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
implicit final class ByteArraySchemaOps(private val self: Array[Byte]) {
|
|
389
|
+
def fromJson[A](implicit schema: Schema[A]): Either[SchemaError, A] = ...
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
The API is the same—just import `zio.blocks.schema._` and the appropriate syntax is provided for your Scala version.
|
|
394
|
+
|
|
395
|
+
## Method Reference
|
|
396
|
+
|
|
397
|
+
| Method | Receiver | Return Type | Description |
|
|
398
|
+
|--------|----------|-------------|-------------|
|
|
399
|
+
| `toJson` | `A` | `Json` | Convert to JSON AST |
|
|
400
|
+
| `toJsonString` | `A` | `String` | Convert to JSON string |
|
|
401
|
+
| `toJsonBytes` | `A` | `Array[Byte]` | Convert to UTF-8 bytes |
|
|
402
|
+
| `fromJson[A]` | `String` | `Either[SchemaError, A]` | Parse JSON string |
|
|
403
|
+
| `fromJson[A]` | `Array[Byte]` | `Either[SchemaError, A]` | Parse JSON bytes |
|
|
404
|
+
| `show` | `A` | `String` | Pretty-print via DynamicValue |
|
|
405
|
+
| `diff` | `A` | `Patch[A]` | Compute patch to another value |
|
|
406
|
+
| `applyPatch` | `A` | `A` | Apply patch (lenient) |
|
|
407
|
+
| `applyPatchStrict` | `A` | `Either[SchemaError, A]` | Apply patch (strict) |
|
|
408
|
+
|
|
409
|
+
All methods require an implicit/given `Schema[A]` in scope.
|