### Running Scala CLI Examples Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Examples are presented as runnable snippets for Scala CLI. Copy the content into a .sc file and run using the provided command. ```bash # scala_version - e.g. {{ scala.2_12 }}, {{ scala.2_13 }} or {{ scala.3 }} # platform - e.g. jvm, scala-js or scala-native scala-cli run --scala $scala_version --platform $platform . ``` -------------------------------- ### Example Structured Log Output for Patcher Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md This is an example of the detailed structured log output generated when macro logging is enabled for a Patcher transformation. It shows the derivation process, rule expansions, and final expression. ```text + Start derivation with context: PatcherContext[A = User, Patch = UserUpdateForm](obj = user, patch = userupdateform)(PatcherConfig( | flags = PatcherFlags(displayMacrosLogging), | preventImplicitSummoningForTypes = None | )) + Deriving Patcher expression for User with patch UserUpdateForm + Deriving Total Transformer expression from java.lang.String to Email with context: | ForTotal[From = java.lang.String, To = Email](src = userupdateform.email)(TransformerConfig( | flags = TransformerFlags(), | instanceFlagOverridden = false, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule + Attempting expansion of rule Subtypes + Rule Subtypes decided to pass on to the next rule + Attempting expansion of rule OptionToOption + Rule OptionToOption decided to pass on to the next rule + Attempting expansion of rule PartialOptionToNonOption + Rule PartialOptionToNonOption decided to pass on to the next rule + Attempting expansion of rule ToOption + Rule ToOption decided to pass on to the next rule + Attempting expansion of rule ValueClassToValueClass + Rule ValueClassToValueClass decided to pass on to the next rule + Attempting expansion of rule ValueClassToType + Rule ValueClassToType decided to pass on to the next rule + Attempting expansion of rule TypeToValueClass + Deriving Total Transformer expression from java.lang.String to java.lang.String with context: | ForTotal[From = java.lang.String, To = java.lang.String](src = userupdateform.email)(TransformerConfig( | flags = TransformerFlags(), | instanceFlagOverridden = false, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule + Attempting expansion of rule Subtypes + Rule Subtypes expanded successfully: userupdateform.email + Derived recursively total expression userupdateform.email + Rule TypeToValueClass expanded successfully: new Email(userupdateform.email) + Deriving Total Transformer expression from scala.Long to Phone with context: | ForTotal[From = scala.Long, To = Phone](src = userupdateform.phone)(TransformerConfig( | flags = TransformerFlags(), | instanceFlagOverridden = false, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule + Attempting expansion of rule Subtypes + Rule Subtypes decided to pass on to the next rule + Attempting expansion of rule OptionToOption ``` -------------------------------- ### Example of Transformer Configuration Type Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/under-the-hood.md Illustrates how configuration types are built using chained type constructors like FieldRenamed and FieldConst, starting from an Empty configuration. ```scala source.withFieldConst(_.a, ???).withFieldRenamed(_.b, _.c).transform // has a Cfg type like TransformerCfg.FieldRenamed[fieldBType, fieldCType, TransformerCfg.FieldConst[fieldAType, TransformerCfg.Empty]] // Let's not dive into how field names are represented. ``` -------------------------------- ### Serve Documentation Locally Source: https://github.com/scalalandio/chimney/blob/master/CONTRIBUTING.md Use the 'just serve' command to build and serve the documentation locally. This command requires Just and Docker to be installed. ```bash cd docs just serve ``` -------------------------------- ### Automatic Derivation Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Demonstrates automatic derivation using `implicitly`. Avoid this pattern as it can lead to circular dependencies. ```scala implicit val typeclass: TypeClass[A] = implicitly[TypeClass[A]] ``` -------------------------------- ### Setup Chimney for Scala 3 Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/quickstart.md Use scala-cli to set up your REPL environment with the specified Scala version and Chimney dependency. Then, import the necessary DSL for transformations. ```bash scala-cli repl --scala "{{ scala.3 }}" --dependency "io.scalaland::chimney::{{ chimney_version() }}" ``` ```scala import io.scalaland.chimney.dsl._ ``` -------------------------------- ### Chimney Counterpart Total Transformations Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Provides the Chimney equivalent for the Ducktape total transformations example. It demonstrates mapping data between different case classes and enums, including overriding specific fields. ```scala // file: snippet.scala - part of Ductape counterpart 1 //> using scala {{ scala.3 }} //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} object wire: final case class Person( firstName: String, lastName: String, paymentMethods: List[wire.PaymentMethod] ) enum PaymentMethod: case Card(name: String, digits: Long) case PayPal(email: String) case Cash object domain: final case class Person( firstName: String, lastName: String, paymentMethods: Vector[domain.PaymentMethod] ) enum PaymentMethod: case PayPal(email: String) case Card(digits: Long, name: String) case Cash @main def example: Unit = { val wirePerson = wire.Person( "John", "Doe", List( wire.PaymentMethod.Cash, wire.PaymentMethod.PayPal("john@doe.com"), wire.PaymentMethod.Card("J. Doe", 23232323) ) ) import io.scalaland.chimney.dsl.* pprint.pprintln( wirePerson.transformInto[domain.Person] ) // expected output: // Person( // firstName = "John", // lastName = "Doe", // paymentMethods = Vector( // Cash, // PayPal(email = "john@doe.com"), // Card(digits = 23232323L, name = "J. Doe") // ) // ) pprint.pprintln( wirePerson .into[domain.Person] .withFieldConst(_.paymentMethods.everyItem.matching[domain.PaymentMethod.PayPal].email, "overridden@email.com") .transform ) // expected output: ``` -------------------------------- ### Ducktape Total Transformations Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Demonstrates total transformations using Ducktape, including field consts and via transformations. This example shows how to map data between different case classes and enums with varying field names and types. ```scala //> using scala {{ scala.3 }} //> using dep io.github.arainko::ducktape::{{ libraries.ducktape }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} object wire: final case class Person( firstName: String, lastName: String, paymentMethods: List[wire.PaymentMethod] ) enum PaymentMethod: case Card(name: String, digits: Long) case PayPal(email: String) case Cash object domain: final case class Person( firstName: String, lastName: String, paymentMethods: Vector[domain.PaymentMethod] ) enum PaymentMethod: case PayPal(email: String) case Card(digits: Long, name: String) case Cash val wirePerson = wire.Person( "John", "Doe", List( wire.PaymentMethod.Cash, wire.PaymentMethod.PayPal("john@doe.com"), wire.PaymentMethod.Card("J. Doe", 23232323) ) ) import io.github.arainko.ducktape.* pprint.pprintln( wirePerson.to[domain.Person] ) // expected output: // Person( // firstName = "John", // lastName = "Doe", // paymentMethods = Vector( // Cash, // PayPal(email = "john@doe.com"), // Card(digits = 23232323L, name = "J. Doe") // ) // ) pprint.pprintln( wirePerson .into[domain.Person] .transform( Field.const(_.paymentMethods.element.at[domain.PaymentMethod.PayPal].email, "overridden@email.com") ) ) // expected output: // Person( // firstName = "John", // lastName = "Doe", // paymentMethods = Vector( // Cash, // PayPal(email = "overridden@email.com"), // Card(digits = 23232323L, name = "J. Doe") // ) // ) pprint.pprintln( wirePerson.via(domain.Person.apply) ) // expected output: // Person( // firstName = "John", // lastName = "Doe", // paymentMethods = Vector( // Cash, // PayPal(email = "john@doe.com"), // Card(digits = 23232323L, name = "J. Doe") // ) // ) pprint.pprintln( wirePerson .intoVia(domain.Person.apply) .transform(Field.const(_.paymentMethods.element.at[domain.PaymentMethod.PayPal].email, "overridden@email.com") ) ) // expected output: // Person( // firstName = "John", // lastName = "Doe", // paymentMethods = Vector( // Cash, // PayPal(email = "overridden@email.com"), // Card(digits = 23232323L, name = "J. Doe") // ) // ) ``` -------------------------------- ### Runtime Data Appending Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/under-the-hood.md Demonstrates how constants and functions are appended as runtime data within the TransformerInto chain, upcasted to Any. ```scala new TransformerInto(source) .methodAppendingValue(constant: Any) .methodAppendingValue(function: Any) // ... ``` -------------------------------- ### Chimney Macro Expansion Log Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md This is an example of the structured log output generated by Chimney when `.enableMacrosLogging` is used. It details the derivation process, rules applied, and field resolutions. ```text + Start derivation with context: ForTotal[From = Bar, To = Foo](src = bar)(TransformerConfig( flags = TransformerFlags(processDefaultValues, displayMacrosLogging), instanceFlagOverridden = true, fieldOverrides = Map(), coproductOverrides = Map(), preventImplicitSummoningForTypes = None )) + Deriving Total Transformer expression from Bar to Foo with context: | ForTotal[From = Bar, To = Foo](src = bar)(TransformerConfig( | flags = TransformerFlags(processDefaultValues, displayMacrosLogging), | instanceFlagOverridden = true, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule - some conditions were fulfilled but at least one failed: Configuration has defined overrides + Attempting expansion of rule Subtypes + Rule Subtypes decided to pass on to the next rule + Attempting expansion of rule OptionToOption + Rule OptionToOption decided to pass on to the next rule + Attempting expansion of rule PartialOptionToNonOption + Rule PartialOptionToNonOption decided to pass on to the next rule + Attempting expansion of rule ToOption + Rule ToOption decided to pass on to the next rule + Attempting expansion of rule ValueClassToValueClass + Rule ValueClassToValueClass decided to pass on to the next rule + Attempting expansion of rule ValueClassToType + Rule ValueClassToType decided to pass on to the next rule + Attempting expansion of rule TypeToValueClass + Rule TypeToValueClass decided to pass on to the next rule + Attempting expansion of rule EitherToEither + Rule EitherToEither decided to pass on to the next rule + Attempting expansion of rule MapToMap + Rule MapToMap decided to pass on to the next rule + Attempting expansion of rule IterableToIterable + Rule IterableToIterable decided to pass on to the next rule + Attempting expansion of rule ProductToProduct + Resolved Bar getters: (`x`: java.lang.String (ConstructorVal, declared), `y`: scala.Int (ConstructorVal, declared), `_1`: java.lang.String (AccessorMethod, declared), `_2`: scala.Int (AccessorMethod, declared)) and Foo constructor (`x`: java.lang.String (ConstructorParameter, default = None), `y`: scala.Int (ConstructorParameter, default = None), `z`: scala.Boolean (ConstructorParameter, default = Some(Foo.$lessinit$greater$default))) + Recursive derivation for field `x`: java.lang.String into matched `x`: java.lang.String + Deriving Total Transformer expression from java.lang.String to java.lang.String with context: | ForTotal[From = java.lang.String, To = java.lang.String](src = bar.x)(TransformerConfig( | flags = TransformerFlags(processDefaultValues, displayMacrosLogging), | instanceFlagOverridden = false, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule + Attempting expansion of rule Subtypes + Rule Subtypes expanded successfully: bar.x + Derived recursively total expression bar.x + Resolved `x` field value to bar.x + Recursive derivation for field `y`: scala.Int into matched `y`: scala.Int + Deriving Total Transformer expression from scala.Int to scala.Int with context: | ForTotal[From = scala.Int, To = scala.Int](src = bar.y)(TransformerConfig( | flags = TransformerFlags(processDefaultValues, displayMacrosLogging), | instanceFlagOverridden = false, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule + Attempting expansion of rule Subtypes + Rule Subtypes expanded successfully: bar.y + Derived recursively total expression bar.y + Resolved `y` field value to bar.y + Resolved `z` field value to Foo.$lessinit$greater$default ``` -------------------------------- ### Chimney Semiautomatic Derivation Methods Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Examples of semiautomatic derivation using `Transformer.derive`, `PartialTransformer.derive`, and `Patcher.derive`. Customization is available via `define`. ```scala // Defaults only: Transformer.derive[From, To] PartialTransformer.derive[From, To] Patcher.derive[A, Patch] // Allows customization: Transformer.define[From, To].buildTransformer PartialTransformer.define[From, To].buildTransformer Patcher.define[A, Patch].buildPatcher ``` -------------------------------- ### Basic Collection Transformation with Chimney Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Demonstrates a straightforward transformation of a `List` of `Foo` objects into a `List` of `Bar` objects using Chimney's `transformInto` method. This example highlights the basic capability of transforming elements within a collection. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} case class Foo(a: Int) case class Bar(a: Int) import io.scalaland.chimney.dsl._ pprint.pprintln( List(Foo(10)).map { foo => foo.transformInto[Bar] } ) // expected output: // List(Bar(a = 10)) ``` -------------------------------- ### Fallible Transformation Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/index.md Demonstrates a fallible transformation from a DTO to a domain object using Chimney's `transformIntoPartial` and handling potential errors. ```scala // file: conversion.sc - part of the demo //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.dsl._ pprint.pprintln( UserDTO( "John", Seq(AddressDTO("Paper St", "Somewhere")), Option(RecoveryMethodDTO.Email(EmailDTO("john@example.com"))) ) .transformIntoPartial[User] .asEither .left .map(_.asErrorPathMessages) ) // expected output: // Right( // value = User( // name = Username(name = "John"), ``` -------------------------------- ### Generated Code for Simple Transformation Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md This is an example of the code Chimney might generate for a straightforward transformation when macros are enabled and no implicits are provided. ```scala val foo = Foo(Foo.Baz("string")) new Bar(new Bar.Baz(foo.baz.a)) ``` -------------------------------- ### Coproduct Configurations with Missing Enum Cases Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md When mapping between coproducts (enums) with different cases, you can configure how to handle missing cases. This example shows mapping `wire.PaymentMethod` to `domain.PaymentMethod` where `Transfer` is missing in the destination. ```scala val transfer = wire.PaymentMethod.Transfer("2764262") val wirePerson = wire.Person( "John", "Doe", List( wire.PaymentMethod.Cash, wire.PaymentMethod.PayPal("john@doe.com"), wire.PaymentMethod.Card("J. Doe", 23232323) ``` -------------------------------- ### Define Recursive Data Types Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Example of defining recursive data types `Foo` and `Bar` in Scala. This serves as a setup for demonstrating recursive transformations. ```scala case class Foo(a: Int, b: Option[Foo]) case class Bar(a: Int, b: Option[Bar]) val foo = Foo(10, Some(Foo(20, None))) // val bar = ??? // how to implement it? ``` -------------------------------- ### Update nested fields with Chimney Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md This example demonstrates how to achieve the same nested field updates as the Quicklens example, but using Chimney. It requires Chimney and pprint dependencies. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} case class Foo(bar: Option[Bar]) case class Bar(baz: List[Baz]) case class Baz(a: Int, b: String, c: Double) val foo = Foo( bar = Some( Bar( baz = List( Baz(a = 1, b = "a", c = 10.0), Baz(a = 2, b = "b", c = 20.0) ) ) ) ) import io.scalaland.chimney.dsl._ pprint.pprintln( foo .into[Foo] .withFieldConst(_.bar.matchingSome.baz.everyItem.a, 10) .withFieldConst(_.bar.matchingSome.baz.everyItem.b, "new") .enableMacrosLogging .transform ) // expected output: // Foo( // bar = Some( // value = Bar(baz = List(Baz(a = 10, b = "new", c = 10.0), Baz(a = 10, b = "new", c = 20.0))) // ) // ) ``` -------------------------------- ### Import Chimney DSL (All-in-one) Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cheatsheet.md Recommended import for summoning and inlined code generation of Transformers, PartialTransformers, and Patchers. ```scala import io.scalaland.chimney.dsl._ ``` -------------------------------- ### Transforming Tuples and Products with Chimney Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Demonstrates various tuple and product transformations using Chimney, including product-to-tuple, tuple-to-product, tuple-to-tuple, and configuring specific fields within tuple transformations. ```scala // file: snippet.scala - part of Ductape counterpart 7 //> using scala {{ scala.3 }} //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} case class Toplevel(int: Int, opt: Option[Int], coll: Vector[Int], level1: Level1) case class Level1(int1: Int, int2: Int) import io.scalaland.chimney.dsl.* @main def example = { // product to tuple pprint.pprintln { val source = (1, 1, List(1), (1, 2, 3)) source.transformInto[Toplevel] } // expected output: // Toplevel(int = 1, opt = Some(value = 1), coll = Vector(1), level1 = Level1(int1 = 1, int2 = 2)) // tuple to product pprint.pprintln { val source = Toplevel(1, Some(1), Vector(1), Level1(1, 2)) source.transformInto[(Int, Option[Int], List[Int], (Int, Int))] } // expected output: // (1, Some(value = 1), List(1), (1, 2)) // tuple to tuple pprint.pprintln { val source = (1, 1, List(1), (1, 2, 3)) source.transformInto[(Int, Option[Int], Vector[Int], (Int, Int))] } // expected output: // (1, Some(value = 1), Vector(1), (1, 2)) // configuring transformations that target a tuple pprint.pprintln { val source = Toplevel(1, Some(1), Vector(1), Level1(1, 2)) source .into[(Int, Option[Int], List[Int], (Int, Int, String))] .withFieldConst(_._4._3, "Missing value!") .transform } // expected output: // (1, Some(value = 1), List(1), (1, 2, "Missing value!")) } ``` -------------------------------- ### Debugging Macro Expansions in Chimney Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md This output shows the step-by-step process of a macro expansion in Chimney. It details which rules are attempted, passed over, or successfully applied during the derivation of a transformation. Use this to understand why a specific transformation might not be working as expected. ```scala + Rule OptionToOption decided to pass on to the next rule + Attempting expansion of rule PartialOptionToNonOption + Rule PartialOptionToNonOption decided to pass on to the next rule + Attempting expansion of rule ToOption + Rule ToOption decided to pass on to the next rule + Attempting expansion of rule ValueClassToValueClass + Rule ValueClassToValueClass decided to pass on to the next rule + Attempting expansion of rule ValueClassToType + Rule ValueClassToType decided to pass on to the next rule + Attempting expansion of rule TypeToValueClass + Deriving Total Transformer expression from scala.Long to scala.Long with context: | ForTotal[From = scala.Long, To = scala.Long](src = userupdateform.phone)(TransformerConfig( | flags = TransformerFlags(), | instanceFlagOverridden = false, | fieldOverrides = Map(), | coproductOverrides = Map(), | preventImplicitSummoningForTypes = None | )) + Attempting expansion of rule Implicit + Rule Implicit decided to pass on to the next rule + Attempting expansion of rule Subtypes + Rule Subtypes expanded successfully: userupdateform.phone + Derived recursively total expression userupdateform.phone + Rule TypeToValueClass expanded successfully: new Phone(userupdateform.phone) + Derived final expression is: | new User(user.id, new Email(userupdateform.email), new Phone(userupdateform.phone)) + Derivation took 0.113354000 s ``` -------------------------------- ### Run All Snippet Tests Source: https://github.com/scalalandio/chimney/blob/master/CONTRIBUTING.md Execute all documentation snippets using Scala CLI. Replace '[version from the artifacts]' with the actual version published locally. The command tests all snippets in the specified documentation directory. ```bash scala-cli run scripts/test-snippets.scala -- --extra "chimney-version=[version from the artifacts]" "$PWD/docs/docs" ``` -------------------------------- ### Java Bean Transformations (Full) Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Demonstrates transforming between Java Beans using Chimney, enabling bean getters and setters. Includes both successful and partial transformations with computed fields. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} import io.scalaland.chimney.dsl._ import io.scalaland.chimney.partial class Foo() { def getA: String = "value" def getB(): Int = 777 } class Bar() { private var a = "" def getA(): String = a def setA(aa: String): Unit = a = aa private var c = 0L def getC: Long = c def setC(cc: Long): Unit = c = cc } // All transformations derived in this scope will see these new flags (Scala 2-only syntax, see cookbook for Scala 3!). implicit val cfg = TransformerConfiguration.default.enableBeanGetters.enableBeanSetters (new Foo()) .into[Bar] .withFieldComputed(_.getC, foo => foo.getB().toLong) .transform // val foo = new Foo() // val bar = new Bar() // bar.setA(foo.getA) // bar.setC(100L) // bar (new Foo()) .intoPartial[Bar] .withFieldComputedPartial(_.getC, foo => partial.Result.fromCatching(foo.getA.toLong)) .transform // val foo = new Foo() // partial.Result.fromCatched(foo.getA.toLong).map { // val bar = new Bar() // bar.setA(foo.getA()) // bar.setC(c) // bar // } ``` -------------------------------- ### Infallible Transformation Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/index.md Demonstrates an infallible transformation from a domain object to a DTO using Chimney's `transformInto`. ```scala // file: conversion.sc - part of the demo //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.dsl._ pprint.pprintln( User( Username("John"), List(Address("Paper St", "Somewhere")), RecoveryMethod.Email("john@example.com") ).transformInto[UserDTO] ) // expected output: // UserDTO( // name = "John", // addresses = List(AddressDTO(street = "Paper St", city = "Somewhere")), // recovery = Some(value = Email(value = EmailDTO(email = "john@example.com"))) // ) ``` -------------------------------- ### Conditional Transformation Example Source: https://github.com/scalalandio/chimney/blob/master/DESIGN.md Illustrates a scenario where a transformation's outcome depends on a condition, demonstrating the use of `withFieldConst` and `transform`. ```scala val expr = Foo(1, "test").into[Bar] if (condition) expr.withFieldConst(_.c, 3.0).transform else expr.withFieldConst(_.c, 4.0).transform ``` -------------------------------- ### Try Chimney in Scala CLI Source: https://context7.com/scalalandio/chimney/llms.txt You can quickly try out Chimney using Scala CLI by specifying the dependency in the `scala-cli repl` command. ```scala scala-cli repl --scala "3.3.7" --dependency "io.scalaland::chimney::" ``` -------------------------------- ### Cats PartialTransformer and Result Instances Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Shows how to use Chimney's PartialTransformer and Cats' partial.Result for transformations that might fail, demonstrating map, dimap, and Arrow.id. Requires cats-core and chimney-cats dependencies. ```scala //> using dep org.typelevel::cats-core::{{ libraries.cats }} //> using dep io.scalaland::chimney-cats::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import cats.syntax.all._ import io.scalaland.chimney.PartialTransformer import io.scalaland.chimney.cats._ val example: PartialTransformer[String, Int] = PartialTransformer.fromFunction[String, Int](_.toInt) pprint.pprintln( example.map(int => int.toDouble).transform("10") ) pprint.pprintln( example.dimap[String, Float](str => str + "00")(int => int.toFloat).transform("10") ) pprint.pprintln( cats.arrow.Arrow[PartialTransformer].id[String].transform("value") ) // expected output: // Value(value = 10.0) // Value(value = 1000.0F) // Value(value = "value") ``` -------------------------------- ### Semi-Automatic Derivation Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Shows semi-automatic derivation by explicitly calling `deriveTypeClass`. This ensures a single, manually created instance is used. ```scala implicit val typeclass: TypeClass[A] = deriveTypeClass[A] ``` -------------------------------- ### Enable Macro Logging for Patcher Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Demonstrates enabling macro logging specifically for a Patcher transformation. This allows detailed inspection of how the Patcher applies updates. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} import io.scalaland.chimney.dsl._ case class Email(address: String) extends AnyVal case class Phone(number: Long) extends AnyVal case class User(id: Int, email: Email, phone: Phone) case class UserUpdateForm(email: String, phone: Long) val user = User(10, Email("abc@@domain.com"), Phone(1234567890L)) val updateForm = UserUpdateForm("xyz@@domain.com", 123123123L) user.using(updateForm).enableMacrosLogging.patch ``` -------------------------------- ### StackOverflowError Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md This error indicates infinite recursion when calling a transformer that was defined recursively. It's a sign that semiautomatic derivation should be used. ```scala java.lang.StackOverflowError ``` -------------------------------- ### Simplest In-Place Mapping with Chimney Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Shows the equivalent in-place mapping using Chimney. This requires the Chimney DSL import and uses the `transformInto` method. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} case class SourceClass(label: String, value: Int) case class TargetClass(label: String, value: Int) import io.scalaland.chimney.dsl._ val source = SourceClass("label", 10) val target = source.transformInto[TargetClass] pprint.pprintln(target) // expected output: // TargetClass(label = "label", value = 10) ``` -------------------------------- ### Global Transformer Configuration Source: https://context7.com/scalalandio/chimney/llms.txt Configure transformer behavior globally using implicit `TransformerConfiguration`. This example shows Scala 2 syntax. ```scala // Global scope config (Scala 2 syntax; see cookbook for Scala 3) implicit val cfg = TransformerConfiguration.default.enableMethodAccessors.enableDefaultValues ``` -------------------------------- ### Using PartialTransformer for Option Unwrapping Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Shows how to use `PartialTransformer` with `failFast = true` and `.asOption` to safely unwrap Options, providing an explicit `Option` result instead of throwing exceptions. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.dsl._ case class Foo(a: Option[String]) case class Bar(a: String) pprint.pprintln( Foo(Some("value")).transformIntoPartial[Bar](failFast = true).asOption ) pprint.pprintln( Foo(None).transformIntoPartial[Bar](failFast = true).asOption ) // expected output: // Some(value = Bar(a = "value")) // None ``` -------------------------------- ### Customize Transformer for Recursive Types Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Shows how to customize a `Transformer` for recursive data types using `.define.buildTransformer`. This example customizes the transformation for the `a` field. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.Transformer import io.scalaland.chimney.dsl._ case class Foo(a: Int, b: Option[Foo]) case class Bar(a: Int, b: Option[Bar]) implicit val foobar: Transformer[Foo, Bar] = Transformer .define[Foo, Bar] .withFieldComputed(_.a, foo => foo.a * 2) .buildTransformer val foo = Foo(10, Some(Foo(20, None))) val bar = foo.transformInto[Bar] pprint.pprintln(bar) // expected output: // Bar(a = 20, b = Some(value = Bar(a = 40, b = None))) ``` -------------------------------- ### Import Protocol Buffers Integration Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/quickstart.md Import the necessary Chimney Protocol Buffers integration into your Scala code. ```scala import io.scalaland.chimney.protobufs._ ``` -------------------------------- ### Define and Use a Total Transformer Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Demonstrates how to manually define a `Transformer` for arbitrary types and use it via an extension method. This is useful when Chimney cannot derive the transformation automatically. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.Transformer class MyType(val a: Int) class MyOtherType(val b: String) { override def toString: String = s"MyOtherType($b)" } // There are better ways of defining implicit Transformer - see Transformer.derive[From, To] and // Transformer.define[From, To].buildTransformer - but for completely arbitrary type it's ok: val transformer: Transformer[MyType, MyOtherType] = (src: MyType) => new MyOtherType(src.a.toString) transformer.transform(new MyType(10)) // new MyOtherType("10") import io.scalaland.chimney.dsl._ // When the compiler can find an implicit Transformer... implicit val transformerAsImplicit: Transformer[MyType, MyOtherType] = transformer // ...we can use this extension method to call it pprint.pprintln( (new MyType(10)).transformInto[MyOtherType] ) // expected output: // MyOtherType(10) ``` -------------------------------- ### Create PartialTransformer using partial.Result utilities Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Demonstrates creating a `PartialTransformer` by directly returning `partial.Result` values for different cases: empty, error string, error throwable, and catching exceptions for valid numeric strings. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.PartialTransformer import io.scalaland.chimney.dsl._ import io.scalaland.chimney.partial implicit val transformer: PartialTransformer[String, Int] = PartialTransformer[String, Int] { str => str match { case "" => partial.Result.fromEmpty case "msg" => partial.Result.fromErrorString("error message") case "error" => partial.Result.fromErrorThrowable(new Throwable("an error happened")) case value => partial.Result.fromCatching(value.toInt) } } pprint.pprintln( List("", "error", "msg", "invaid").transformIntoPartial[List[Int]].asEitherErrorPathMessageStrings ) // expected output: // Left( // value = List( // ("(0)", "empty value"), // ("(1)", "an error happened"), // ("(2)", "error message"), // ("(3)", "For input string: \"invaid\"") // ) // ) ``` -------------------------------- ### Define Monix Newtype Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Define a newtype with validation using Monix's Newtypes library. This example shows how to create a `Username` type that cannot be empty. ```scala //> using dep io.monix::newtypes-core::0.2.3 import monix.newtypes._ type Username = Username.Type object Username extends NewtypeValidated[String] { def apply(value: String): Either[BuildFailure[Type], Type] = if (value.isEmpty) Left(BuildFailure("Username cannot be empty")) else Right(unsafeCoerce(value)) } ``` -------------------------------- ### Ducktape Fallible Transformer Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Illustrates how Ducktape uses `Transformer.Fallible` with `Either` for fallible transformations, specifically for custom types like `NonEmptyString` and `Positive`. ```scala //> using scala {{ scala.3 }} //> using dep io.github.arainko::ducktape::{{ libraries.ducktape }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} object newtypes: opaque type NonEmptyString <: String = String object NonEmptyString: def create(value: String): Either[String, NonEmptyString] = Either.cond(!value.isBlank, value, s"not a non-empty string") opaque type Positive <: Long = Long object Positive: def create(value: Long): Either[String, Positive] = Either.cond(value > 0, value, "not a positive long") object wire: final case class Person( firstName: String, lastName: String, paymentMethods: List[wire.PaymentMethod] ) enum PaymentMethod: case Card(name: String, digits: Long) case PayPal(email: String) case Cash object domain: final case class Person( firstName: newtypes.NonEmptyString, lastName: newtypes.NonEmptyString, paymentMethods: Vector[domain.PaymentMethod] ) enum PaymentMethod: case PayPal(email: newtypes.NonEmptyString) case Card(digits: newtypes.Positive, name: newtypes.NonEmptyString) case Cash val wirePerson = wire.Person( "John", "Doe", List( wire.PaymentMethod.Cash, wire.PaymentMethod.PayPal("john@doe.com"), wire.PaymentMethod.Card("J. Doe", 23232323) ) ) import io.github.arainko.ducktape.* // expand the 'create' method into an instance of Transformer.Fallible // this is a key component in making those transformations automatic given failFastNonEmptyString: Transformer.Fallible[[a] =>> Either[String, a], String, newtypes.NonEmptyString] = newtypes.NonEmptyString.create ``` -------------------------------- ### Chimney Error Message Example Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/quickstart.md Illustrates the error message provided by Chimney when a transformation cannot be automatically derived, highlighting missing fields or inaccessible constructors. ```scala apiUser.transformInto[User] // Chimney can't derive transformation from ApiUser to User // // User // id: java.util.UUID - no accessor named id in source type ApiUser // // Consult https://chimney.readthedocs.io for usage examples. ``` -------------------------------- ### Transform Tuples to Products with Ducktape Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Shows how to convert tuples to product types (case classes) using Ducktape's `to` method. ```scala // file: snippet.scala - part of Ductape counterpart 6 //> using scala {{ scala.3 }} //> using dep io.github.arainko::ducktape::{{ libraries.ducktape }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} case class Toplevel(int: Int, opt: Option[Int], coll: Vector[Int], level1: Level1) case class Level1(int1: Int, int2: Int) import io.github.arainko.ducktape.* @main def example = { // tuple to product pprint.pprintln { val source = Toplevel(1, Some(1), Vector(1), Level1(1, 2)) source.to[(Int, Option[Int], List[Int], (Int, Int))] } // expected output: // (1, Some(value = 1), List(1), (1, 2)) ``` -------------------------------- ### Avoid Recursive Calls on Implicits Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Defining an implicit transformer that directly calls itself can lead to stack overflows or other runtime errors. This example shows the problematic pattern. ```scala implicit val t: Transformer[Foo, Bar] = foo => foo.transformInto[Bar] ``` ```scala implicit val t: Transformer[Foo, Bar] = foo => foo.into[Bar].transform ``` -------------------------------- ### Ducktyping with Accumulating Fallible Transformers Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Shows how to use accumulating fallible transformers for error collection. This example requires custom type definitions for `newtypes.NonEmptyString` and `newtypes.Positive`. ```scala given accumulatingNonEmptyString: Transformer.Fallible[[a] =>> Either[List[String], a], String, newtypes.NonEmptyString] = newtypes.NonEmptyString.create(_).left.map(_ :: Nil) given accumulatingPositive: Transformer.Fallible[[a] =>> Either[List[String], a], Long, newtypes.Positive] = newtypes.Positive.create(_).left.map(_ :: Nil) pprint.pprintln { Mode.Accumulating.Either[String, List].locally { wirePerson.fallibleTo[domain.Person] } } ``` -------------------------------- ### Cats Instances for Transformer and PartialTransformer Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/cookbook.md Demonstrates how to use Chimney's Cats instances for composing transformations. Requires cats-core and chimney-cats dependencies. ```scala //> using dep org.typelevel::cats-core::{{ libraries.cats }} //> using dep io.scalaland::chimney-cats::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import cats.syntax.all._ import io.scalaland.chimney.{PartialTransformer, Transformer} import io.scalaland.chimney.cats._ case class Foo(a: Int) pprint.pprintln( cats.arrow.Arrow[Transformer].lift[Foo, Int](_.a) .andThen(cats.arrow.Arrow[Transformer].lift[Int, String](_.toString)) .transform(Foo(10)) ) // expected output: // "10" ``` ```scala pprint.pprintln( cats.arrow.Arrow[PartialTransformer].lift[Int, String](_.toString) .compose(cats.arrow.Arrow[PartialTransformer].lift[Foo, Int](_.a)) .transform(Foo(10)) .asOption ) // expected output: // Some(value = "10") ``` -------------------------------- ### Transforming Between Option Types Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/supported-transformations.md Demonstrates direct transformation between Option[Foo] and Option[Bar] using transformInto and into.transform. Also shows partial transformation and handling of None. ```scala //> using dep io.scalaland::chimney::{{ chimney_version() }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} import io.scalaland.chimney.dsl._ case class Foo(a: String) case class Bar(a: String) pprint.pprintln( Option(Foo("value")).transformInto[Option[Bar]] ) pprint.pprintln( (None: Option[Foo]).transformInto[Option[Bar]] ) // expected output: // Some(value = Bar(a = "value")) // None pprint.pprintln( Option(Foo("value")).into[Option[Bar]].transform ) pprint.pprintln( (None: Option[Foo]).into[Option[Bar]].transform ) // expected output: // Some(value = Bar(a = "value")) // None pprint.pprintln( Option(Foo("value")).transformIntoPartial[Option[Bar]].asEither ) pprint.pprintln( (None: Option[Foo]).transformIntoPartial[Option[Bar]].asEither ) // expected output: // Right(value = Some(value = Bar(a = "value"))) // Right(value = None) pprint.pprintln( Option(Foo("value")).intoPartial[Option[Bar]].transform.asEither ) pprint.pprintln( (None: Option[Foo]).intoPartial[Option[Bar]].transform.asEither ) // expected output: // Right(value = Some(value = Bar(a = "value"))) // Right(value = None) import io.scalaland.chimney.{Transformer, PartialTransformer} // If we want to reuse Transformer, we can create implicits using: val totalTransformer: Transformer[Option[Foo], Option[Bar]] = Transformer.derive[Option[Foo], Option[Bar]] val partialTransformer: PartialTransformer[Option[Foo], Option[Bar]] = PartialTransformer.derive[Option[Foo], Option[Bar]] // or (if you want to pass overrides): val totalTransformer2: Transformer[Option[Foo], Option[Bar]] = Transformer.define[Option[Foo], Option[Bar]] .buildTransformer val partialTransformer2: PartialTransformer[Option[Foo], Option[Bar]] = PartialTransformer.define[Option[Foo], Option[Bar]] .buildTransformer ``` -------------------------------- ### Transform Products to Tuples with Ducktape Source: https://github.com/scalalandio/chimney/blob/master/docs/docs/troubleshooting.md Demonstrates converting product types (case classes) to tuples using Ducktape's `to` method. ```scala // file: snippet.scala - part of Ductape counterpart 6 //> using scala {{ scala.3 }} //> using dep io.github.arainko::ducktape::{{ libraries.ducktape }} //> using dep com.lihaoyi::pprint::{{ libraries.pprint }} case class Toplevel(int: Int, opt: Option[Int], coll: Vector[Int], level1: Level1) case class Level1(int1: Int, int2: Int) import io.github.arainko.ducktape.* @main def example = { // product to tuple pprint.pprintln { val source = (1, 1, List(1), (1, 2, 3)) source.to[Toplevel] } // expected output: // Toplevel(int = 1, opt = Some(value = 1), coll = Vector(1), level1 = Level1(int1 = 1, int2 = 2)) ```