@zio.dev/zio-blocks 0.0.22 → 0.0.25

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.
@@ -193,6 +193,7 @@ import zio.blocks.schema.DynamicValue.Null
193
193
  import zio.blocks.schema.binding.*
194
194
  import zio.blocks.schema.derive.Deriver
195
195
  import zio.blocks.typeid.TypeId
196
+ import zio.blocks.docs.Doc
196
197
 
197
198
  object DeriveShow extends Deriver[Show] {
198
199
 
@@ -1450,7 +1451,7 @@ Now we can use the derived `Gen[Person]` instance to generate random `Person` va
1450
1451
 
1451
1452
  ```scala
1452
1453
  val random = new Random(42) // Seeded for reproducible output
1453
- // random: Random = scala.util.Random@1d9748ca
1454
+ // random: Random = scala.util.Random@280d230c
1454
1455
 
1455
1456
  Person.gen.generate(random)
1456
1457
  // res14: Person = Person(name = "p", age = -1360544799)
@@ -1598,12 +1599,13 @@ val tc: TC[A] = schema.deriving(deriver)
1598
1599
  .derive // finalize the derivation
1599
1600
  ```
1600
1601
 
1601
- The `DerivationBuilder` offers two overloaded `instance` methods for providing custom type class instances:
1602
+ The `DerivationBuilder` offers three overloaded `instance` methods for providing custom type class instances:
1602
1603
 
1603
1604
  ```scala
1604
1605
  final case class DerivationBuilder[TC[_], A](...) {
1605
1606
  def instance[B](optic: Optic[A, B], instance: => TC[B]): DerivationBuilder[TC, A]
1606
- def instance[B](typeId: TypeId[B], instance: => TC[B]): DerivationBuilder[TC, A]
1607
+ def instance[B](typeId: TypeId[B], instance: => TC[B]): DerivationBuilder[TC, A]
1608
+ def instance[P, B](typeId: TypeId[P], termName: String, instance: => TC[B]): DerivationBuilder[TC, A]
1607
1609
  }
1608
1610
  ```
1609
1611
 
@@ -1695,15 +1697,38 @@ personShow.show(Person("Alice", 30))
1695
1697
  // res32: String = "Person(name = \"Alice\", age = #30)"
1696
1698
  ```
1697
1699
 
1700
+ ### Overriding by TypeId and Term Name
1701
+
1702
+ The third overload takes a `TypeId[P]` identifying the **parent** record or variant and a `termName` string identifying a field or case within it. This provides medium precision between optic-based (exact path) and type-based (all occurrences) overrides, and is useful when you want to override a specific field across all locations where the parent type appears without having to enumerate each path with an optic:
1703
+
1704
+ ```scala
1705
+ val customAgeShow: Show[Int] = new Show[Int] {
1706
+ def show(value: Int): String = s"age=$value"
1707
+ }
1708
+
1709
+ val personShow: Show[Person] = Person.schema
1710
+ .deriving(DeriveShow)
1711
+ .instance(Person.schema.reflect.typeId, "age", customAgeShow)
1712
+ .derive
1713
+ ```
1714
+
1715
+ The `typeId` refers to the parent record/variant type (here `Person`), not the field type. If no term with the given name exists in the parent type, the override is silently ignored.
1716
+
1717
+ ```scala
1718
+ personShow.show(Person("Alice", 30))
1719
+ // res33: String = "Person(name = \"Alice\", age = age=30)"
1720
+ ```
1721
+
1698
1722
  ### Resolution Order
1699
1723
 
1700
1724
  When the derivation engine encounters a schema node, it resolves the type class instance using the following priority order:
1701
1725
 
1702
1726
  1. **Optic-based override** (most precise): If an instance override was registered using an optic that matches the current path in the schema tree, that instance is used.
1703
- 2. **TypeId-based override** (more general): If no optic-based match is found, it checks for an instance override registered by type ID.
1704
- 3. **Automatic derivation** (default): If no override is found, the deriver's method (e.g., `derivePrimitive`, `deriveRecord`) is called to automatically derive the instance.
1727
+ 2. **TypeId + term-name override** (medium precision): If no optic-based match is found, it checks for an override registered by parent type ID and term name.
1728
+ 3. **TypeId-based override** (more general): If no term-name match is found, it checks for an instance override registered by type ID.
1729
+ 4. **Automatic derivation** (default): If no override is found, the deriver's method (e.g., `derivePrimitive`, `deriveRecord`) is called to automatically derive the instance.
1705
1730
 
1706
- This means you can set a global override by type and then selectively refine specific fields using optics:
1731
+ This means you can set a global override by type and then selectively refine specific fields using optics or term names:
1707
1732
 
1708
1733
  ```scala
1709
1734
  val companyShow: Show[Company] = Company.schema
@@ -1721,7 +1746,7 @@ In this example, all `String` fields use single quotes, except for the CEO's nam
1721
1746
 
1722
1747
  ```scala
1723
1748
  companyShow.show(Company(Person("Alice", 30), "tech"))
1724
- // res33: String = "Company(ceo = Person(name = ALICE, age = 30), industry = 'tech')"
1749
+ // res34: String = "Company(ceo = Person(name = ALICE, age = 30), industry = 'tech')"
1725
1750
  ```
1726
1751
 
1727
1752
  ### Chaining Multiple Overrides
@@ -1742,7 +1767,7 @@ val personShow: Show[Person] = Person.schema
1742
1767
 
1743
1768
  ```scala
1744
1769
  personShow.show(Person("Alice", 30))
1745
- // res34: String = "Person(name = <<Alice>>, age = age=30)"
1770
+ // res35: String = "Person(name = <<Alice>>, age = age=30)"
1746
1771
  ```
1747
1772
 
1748
1773
  ## Custom Modifiers
@@ -1755,12 +1780,13 @@ While modifiers can be attached to schemas directly using Scala annotations (e.g
1755
1780
  - You need different modifiers for different derivation contexts (e.g., one JSON codec with renamed fields, another without)
1756
1781
  - You want to keep the schema clean and push format-specific concerns into the derivation layer
1757
1782
 
1758
- The `DerivationBuilder` offers two overloaded `modifier` methods:
1783
+ The `DerivationBuilder` offers three overloaded `modifier` methods:
1759
1784
 
1760
1785
  ```scala
1761
1786
  final case class DerivationBuilder[TC[_], A](...) {
1762
- def modifier[B](typeId: TypeId[B], modifier: Modifier.Reflect): DerivationBuilder[TC, A]
1763
- def modifier[B](optic: Optic[A, B], modifier: Modifier) : DerivationBuilder[TC, A]
1787
+ def modifier[B](typeId: TypeId[B], modifier: Modifier.Reflect): DerivationBuilder[TC, A]
1788
+ def modifier[B](optic: Optic[A, B], modifier: Modifier): DerivationBuilder[TC, A]
1789
+ def modifier[B](typeId: TypeId[B], termName: String, modifier: Modifier.Term): DerivationBuilder[TC, A]
1764
1790
  }
1765
1791
  ```
1766
1792
 
@@ -1822,7 +1848,7 @@ val user = User(1L, "Alice", "alice@example.com", 95.5)
1822
1848
  // internalScore = 95.5
1823
1849
  // )
1824
1850
  new String(jsonCodec.encode(user), "UTF-8")
1825
- // res35: String = "{\"id\":1,\"full_name\":\"Alice\",\"email\":\"alice@example.com\"}"
1851
+ // res36: String = "{\"id\":1,\"full_name\":\"Alice\",\"email\":\"alice@example.com\"}"
1826
1852
  ```
1827
1853
 
1828
1854
  ### Adding Modifiers by TypeId
@@ -1837,6 +1863,18 @@ val jsonCodec: JsonBinaryCodec[User] = User.schema
1837
1863
  .derive
1838
1864
  ```
1839
1865
 
1866
+ ### Adding Modifiers by TypeId and Term Name
1867
+
1868
+ The `modifier` method with `TypeId` and `termName` allows you to add a `Modifier.Term` to a specific field or case identified by name inside a parent type identified by its `TypeId`. This is useful when you want to target a specific term without constructing an optic for it. The `typeId` refers to the parent record/variant type that owns the term. If no term with the given name exists in the parent type, the modifier is silently ignored:
1869
+
1870
+ ```scala
1871
+ val jsonCodec: JsonBinaryCodec[User] = User.schema
1872
+ .deriving(JsonBinaryCodecDeriver)
1873
+ .modifier(User.schema.reflect.typeId, "name", Modifier.rename("full_name"))
1874
+ .modifier(User.schema.reflect.typeId, "internalScore", Modifier.transient())
1875
+ .derive
1876
+ ```
1877
+
1840
1878
  ## Derivation Process In-Depth
1841
1879
 
1842
1880
  Until now, we learned how to implement the `Deriver` methods for different schema patterns. But we haven't yet discussed how the overall derivation process works. In this section, we will go through the main steps of derivation in detail.