aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorMathias <mathias@decodified.com>2017-12-14 14:20:39 +0100
committerMathias <mathias@decodified.com>2017-12-14 14:26:54 +0100
commit579cdb1281e7f3e16b13abc591c5e06b1aa32db5 (patch)
treeb0575afd76ebcaa14a09cb42b2ea79f6b916c682 /examples
parent379f0075ca8042945d8ff89212536894a96f56a8 (diff)
downloadmagnolia-579cdb1281e7f3e16b13abc591c5e06b1aa32db5.tar.gz
magnolia-579cdb1281e7f3e16b13abc591c5e06b1aa32db5.tar.bz2
magnolia-579cdb1281e7f3e16b13abc591c5e06b1aa32db5.zip
Add `CaseClass.rawConstruct` and new `Patcher` example
Diffstat (limited to 'examples')
-rw-r--r--examples/shared/src/main/scala/patch.scala69
1 files changed, 69 insertions, 0 deletions
diff --git a/examples/shared/src/main/scala/patch.scala b/examples/shared/src/main/scala/patch.scala
new file mode 100644
index 0000000..668a2e2
--- /dev/null
+++ b/examples/shared/src/main/scala/patch.scala
@@ -0,0 +1,69 @@
+package magnolia.examples
+
+import scala.language.experimental.macros
+import magnolia._
+
+/**
+ * Type class for copying an instance of some type `T`,
+ * thereby replacing certain fields with other values.
+ */
+sealed abstract class Patcher[T] {
+
+ /**
+ * Returns a copy of `value` whereby all non-null elements of `fieldValues`
+ * replace the respective fields of `value`.
+ * For all null elements of `fieldValues` the original value of the
+ * respective field of `value` is maintained.
+ *
+ * If the size of `fieldValues` doesn't exactly correspond to the
+ * number of fields of `value` an [[IllegalArgumentException]] is thrown.
+ */
+ def patch(value: T, fieldValues: Seq[Any]): T
+}
+
+object Patcher extends LowerPriorityPatcher {
+
+ type Typeclass[T] = Patcher[T]
+
+ def combine[T](ctx: CaseClass[Patcher, T]): Patcher[T] =
+ new Patcher[T] {
+ def patch(value: T, fieldValues: Seq[Any]): T = {
+ if (fieldValues.lengthCompare(ctx.parameters.size) != 0) {
+ throw new IllegalArgumentException(
+ s"Cannot patch value `$value`, expected ${ctx.parameters.size} fields but got ${fieldValues.size}")
+ }
+ val effectiveFields = ctx.parameters.zip(fieldValues).map {
+ case (param, x) => if (x.asInstanceOf[AnyRef] ne null) x else param dereference value
+ }
+ ctx.rawConstruct(effectiveFields)
+ }
+ }
+
+ def dispatch[T](ctx: SealedTrait[Patcher, T]): Patcher[T] =
+ new Patcher[T] {
+ def patch(value: T, fieldValues: Seq[Any]): T =
+ ctx.dispatch(value)(sub ⇒ sub.typeclass.patch(sub cast value, fieldValues))
+ }
+
+ implicit def gen[T]: Patcher[T] = macro Magnolia.gen[T]
+}
+
+sealed abstract class LowerPriorityPatcher {
+
+ private[this] val _forSingleValue =
+ new Patcher[Any] {
+ def patch(value: Any, fieldValues: Seq[Any]): Any = {
+ if (fieldValues.lengthCompare(1) != 0)
+ throw new IllegalArgumentException(
+ s"Cannot patch single value `$value` with patch sequence of size ${fieldValues.size}")
+ val head = fieldValues.head
+ if (head.getClass != value.getClass)
+ throw new IllegalArgumentException(
+ s"Illegal patch value type. Expected `${value.getClass}` but got `${head.getClass}`")
+ head
+ }
+ }
+
+ // once https://github.com/propensive/magnolia/issues/58 is fixed this can be marked `implicit`
+ def forSingleValue[T]: Patcher[T] = _forSingleValue.asInstanceOf[Patcher[T]]
+} \ No newline at end of file