Skip to content

Commit

Permalink
Add documentation for DefaultValueForType flag and DefaultValue type …
Browse files Browse the repository at this point in the history
…class, fix a few overlooked TODOs
  • Loading branch information
MateuszKubuszok committed Jun 27, 2024
1 parent 8fceee8 commit 606b60b
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ final class TransformerDefinition[From, To, Overrides <: TransformerOverrides, F
/** Use `FromSubtype` in `From` as a source for `ToSubtype` in `To`.
*
* @see
* [[https://chimney.readthedocs.io/supported-transformations/#TODO]] for more details
* [[https://chimney.readthedocs.io/supported-transformations/#handling-a-specific-sealed-subtype-by-a-specific-target-subtype]]
* for more details
*
* @tparam FromSubtype
* type of sealed/enum instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ final class TransformerInto[From, To, Overrides <: TransformerOverrides, Flags <
/** Use `FromSubtype` in `From` as a source for `ToSubtype` in `To`.
*
* @see
* [[https://chimney.readthedocs.io/supported-transformations/#TODO]] for more details
* [[https://chimney.readthedocs.io/supported-transformations/#handling-a-specific-sealed-subtype-by-a-specific-target-subtype]]
* for more details
*
* @tparam FromSubtype
* type of sealed/enum instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.scalaland.chimney.integrations
/** Tells Chimney how to provide default value of type `Value` if flag allows them but field does not define it.
*
* @see
* [[https://chimney.readthedocs.io/cookbook/#custom-default-values]] for more details TODO
* [[https://chimney.readthedocs.io/cookbook/#custom-default-values]] for more details
*
* @tparam Value
* type of default value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ private[compiletime] trait TransformSealedHierarchyToSealedHierarchyRuleModule {
}
)
case _ =>
DerivationResult.assertionError("TODO")
// $COVERAGE-OFF$should never happen unless someone mess around with type-level representation
DerivationResult.assertionError(s"Unexpected path: $targetPath")
// $COVERAGE-ON$
})
}
}
Expand Down
47 changes: 46 additions & 1 deletion docs/docs/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -707,14 +707,29 @@ There are 2 ways in which Chimney could handle this issue:

- using [default values](supported-transformations.md#allowing-fallback-to-the-constructors-default-values)

!!! example
!!! example "Globally enabled default values"

```scala
domain
.Address("a", "b")
.into[protobuf.Address]
.enableDefaultValues
.transform

// can be set up for the whole scope with:
// implicit val cfg = TransformerConfiguration.default.enableDefaultValues
```

!!! example "Default values scoped only to UnknownFieldSet"

```scala
domain
.Address("a", "b")
.into[protobuf.Address]
.enableDefaultValueOfType[scalapb.UnknownFieldSet]
.transform
// can be set up for the whole scope with:
// implicit val cfg = TransformerConfiguration.default.enableDefaultValueOfType[scalapb.UnknownFieldSet]
```

- manually [setting this one field](supported-transformations.md#wiring-the-constructors-parameter-to-a-provided-value)_
Expand Down Expand Up @@ -1319,6 +1334,36 @@ We can validate using the dedicated type class (`Validate`), while extraction is
}
```

## Custom default values

If you are providing integration for a type which you do not control, and you'd like to let your users fall back
to default values when using Chimney, but the type does not define them - it might be still possible to provide them
with `io.scalaland.chimney.integrations.DefaultValue`. It could look like this:

!!! example

```scala
//> using dep io.scalaland::chimney::{{ chimney_version() }}

// Types which we cannot simply edit: come from external library, codegen, etc.

case class MyType(int: Int)
case class Foo(a: Int)
case class Bar(a: Int, b: MyType)

// Our integration:
implicit val defaultMyType: io.scalaland.chimney.integrations.DefaultValue[MyType] = () => MyType(0)

// Remember that default values has to be enabled!
import io.scalaland.chimney.dsl._
Foo(10).into[Bar].enableDefaultValues.transform // Bar(10, MyType(0))
Foo(10).into[Bar].enableDefaultValueOfType[MyType].transform // Bar(10, MyType(0))
```

Keep in mind, that such provision works for every constructor which has an argument of such type not matched with source
value, so it's only safe to use when in the scope which sees such implicit all derivations would only need default value
of this type, rather than convert it from something else.

## Custom optional types

In case your library/domain defines custom Optional types, you can provide your own handling of such types through
Expand Down
23 changes: 21 additions & 2 deletions docs/docs/supported-transformations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1001,11 +1001,30 @@ If the flag was enabled in the implicit config it can be disabled with `.disable
// Target
// c: scala.Long - no accessor named c in source type Source
//
// There are default values for c, the constructor argument/setter in Target. Consider using .enableDefaultValues.
// There are default values for c, the constructor argument/setter in Target. Consider using .enableDefaultValues or .enableDefaultValueForType.
//
// Consult https://chimney.readthedocs.io for usage examples.
```

If enabling global values globally, seems too dangerous, you can also limit the scope of their usage, by enabling only
default values of one particular type:

!!! example

```scala
//> using dep io.scalaland::chimney::{{ chimney_version() }}
import io.scalaland.chimney.dsl._

case class Source(a: String, b: Int)
case class Target(a: String, b: Int = 0, c: Long = 0L)

Source("value", 128).into[Target].enableDefaultValueOfType[Long].transform
// val source = Source("value", 128)
// Target(source.a, source.b /* c is filled by the default value */)
Source("value", 128).intoPartial[Target].enableDefaultValueOfType[Long].transform
// val source = Source("value", 128)
// partial.Result.fromValue(Target(source.a, source.b /* c is filled by the default value */))
```

### Allowing fallback to `None` as the constructor's argument

Expand Down Expand Up @@ -1099,7 +1118,7 @@ If the flag was enabled in the implicit config it can be disabled with `.disable
// Bar
// b: scala.Option[java.lang.String] - no accessor named b in source type Foo
//
// There are default values for b, the constructor argument/setter in Bar. Consider using .enableDefaultValues.
// There are default values for b, the constructor argument/setter in Bar. Consider using .enableDefaultValues or .enableDefaultValueForType.
//
// There are default optional values available for b, the constructor argument/setter in Bar. Consider using .enableOptionDefaultsToNone.
//
Expand Down

0 comments on commit 606b60b

Please sign in to comment.