From 0acde1ffc93bf98b74f92502aca8293eefd0aa4a Mon Sep 17 00:00:00 2001 From: Sebastian Haracz Date: Wed, 9 Jul 2025 12:16:22 +0200 Subject: [PATCH 1/3] Rest wrapper implicits --- .../io/udash/rest/RestDataCompanion.scala | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala b/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala index 291d52204..ae2fe4b64 100644 --- a/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala +++ b/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala @@ -4,7 +4,7 @@ package rest import com.avsystem.commons.meta.MacroInstances import com.avsystem.commons.misc.{AbstractValueEnumCompanion, ValueEnum, ValueOf} import com.avsystem.commons.rpc.{AsRaw, AsReal} -import com.avsystem.commons.serialization.{GenCodec, TransparentWrapperCompanion} +import com.avsystem.commons.serialization.{GenCodec, TransparentWrapperCompanion, TransparentWrapping} import io.udash.rest.openapi.* import io.udash.rest.openapi.RestStructure.NameAndAdjusters import io.udash.rest.raw.{HttpBody, JsonValue, PlainValue, RestResponse, StreamedBody, StreamedRestResponse} @@ -47,28 +47,18 @@ abstract class RestDataCompanionWithDeps[D, T](implicit ) extends AbstractRestDataCompanion[(DefaultRestImplicits, D), T]((DefaultRestImplicits, deps.value)) /** - * Base class for companion objects of wrappers over other data types (i.e. case classes with single field). - * This companion ensures instances of all the REST typeclasses (serialization, schema, etc.) for wrapping type - * assuming that these instances are available for the wrapped type. - * - * Using this base companion class makes the wrapper class effectively "transparent", i.e. as if it was annotated with - * [[com.avsystem.commons.serialization.transparent transparent]] annotation. + * These implicits must be specialized for every raw type (PlainValue, JsonValue, etc.) because + * it lifts their priority. Unfortunately, controlling implicit priority is not pretty. + * Also, it's probably good that we explicitly enable derivation only for REST-related raw types + * and not for all raw types - this avoids possible interference with other features using RPC. * - * @example - * {{{ - * case class UserId(id: String) extends AnyVal - * object UserId extends RestDataWrapperCompanion[String, UserId] - * }}} + * Seperated from [[RestDataWrapperCompanion]] to allow creating custom companion wrappers. */ -abstract class RestDataWrapperCompanion[Wrapped, T](implicit - instances: MacroInstances[DefaultRestImplicits, () => NameAndAdjusters[T]] -) extends TransparentWrapperCompanion[Wrapped, T] { - private def nameAndAdjusters: NameAndAdjusters[T] = instances(DefaultRestImplicits, this).apply() +trait RestDataWrapperImplicits[Wrapped, T] { + protected def nameAndAdjusters: NameAndAdjusters[T] + protected def wrapping: TransparentWrapping[Wrapped, T] - // These implicits must be specialized for every raw type (PlainValue, JsonValue, etc.) because - // it lifts their priority. Unfortunately, controlling implicit priority is not pretty. - // Also, it's probably good that we explicitly enable derivation only for REST-related raw types - // and not for all raw types - this avoids possible interference with other features using RPC. + private implicit def wrappingAsImplicit: TransparentWrapping[Wrapped, T] = wrapping implicit def plainAsRaw(implicit wrappedAsRaw: AsRaw[PlainValue, Wrapped]): AsRaw[PlainValue, T] = AsRaw.fromTransparentWrapping @@ -122,6 +112,27 @@ abstract class RestDataWrapperCompanion[Wrapped, T](implicit wrappedResponses.responses(resolver, ws => schemaTransform(nameAndAdjusters.restSchema(ws))) } +/** + * Base class for companion objects of wrappers over other data types (i.e. case classes with single field). + * This companion ensures instances of all the REST typeclasses (serialization, schema, etc.) for wrapping type + * assuming that these instances are available for the wrapped type. + * + * Using this base companion class makes the wrapper class effectively "transparent", i.e. as if it was annotated with + * [[com.avsystem.commons.serialization.transparent transparent]] annotation. + * + * @example + * {{{ + * case class UserId(id: String) extends AnyVal + * object UserId extends RestDataWrapperCompanion[String, UserId] + * }}} + */ +abstract class RestDataWrapperCompanion[Wrapped, T](implicit + instances: MacroInstances[DefaultRestImplicits, () => NameAndAdjusters[T]] +) extends TransparentWrapperCompanion[Wrapped, T] with RestDataWrapperImplicits[Wrapped, T] { + override protected def nameAndAdjusters: NameAndAdjusters[T] = instances(DefaultRestImplicits, this).apply() + override protected def wrapping: TransparentWrapping[Wrapped, T] = this +} + /** * Base class for companion objects of enum types [[ValueEnum]] which are used as * parameter or result types in REST API traits. Automatically provides instance of From 2a7b8c5f277f76f1d8b7cad45aaee91320683be6 Mon Sep 17 00:00:00 2001 From: Sebastian Haracz Date: Wed, 9 Jul 2025 12:17:22 +0200 Subject: [PATCH 2/3] Rest wrapper implicits - nits --- rest/src/main/scala/io/udash/rest/RestDataCompanion.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala b/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala index ae2fe4b64..2ad146353 100644 --- a/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala +++ b/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala @@ -43,7 +43,8 @@ abstract class RestDataCompanion[T](implicit * It must be a singleton object type, i.e. `SomeObject.type`. */ abstract class RestDataCompanionWithDeps[D, T](implicit - deps: ValueOf[D], instances: MacroInstances[(DefaultRestImplicits, D), CodecWithStructure[T]] + deps: ValueOf[D], + instances: MacroInstances[(DefaultRestImplicits, D), CodecWithStructure[T]], ) extends AbstractRestDataCompanion[(DefaultRestImplicits, D), T]((DefaultRestImplicits, deps.value)) /** From e05803579698f176869cb2c5977fd396bf93c8ec Mon Sep 17 00:00:00 2001 From: Sebastian Haracz Date: Wed, 9 Jul 2025 12:18:29 +0200 Subject: [PATCH 3/3] Rest wrapper implicits - nits --- rest/src/main/scala/io/udash/rest/RestDataCompanion.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala b/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala index 2ad146353..af1f2331d 100644 --- a/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala +++ b/rest/src/main/scala/io/udash/rest/RestDataCompanion.scala @@ -130,8 +130,8 @@ trait RestDataWrapperImplicits[Wrapped, T] { abstract class RestDataWrapperCompanion[Wrapped, T](implicit instances: MacroInstances[DefaultRestImplicits, () => NameAndAdjusters[T]] ) extends TransparentWrapperCompanion[Wrapped, T] with RestDataWrapperImplicits[Wrapped, T] { - override protected def nameAndAdjusters: NameAndAdjusters[T] = instances(DefaultRestImplicits, this).apply() - override protected def wrapping: TransparentWrapping[Wrapped, T] = this + override protected final def nameAndAdjusters: NameAndAdjusters[T] = instances(DefaultRestImplicits, this).apply() + override protected final def wrapping: TransparentWrapping[Wrapped, T] = this } /**