Skip to content

Commit 456a54a

Browse files
authored
Merge pull request #407 from AVSystem/mongo-poly-data
`MongoPolyDataCompanion`
2 parents 3346cd1 + 4f3f869 commit 456a54a

File tree

7 files changed

+103
-7
lines changed

7 files changed

+103
-7
lines changed

commons-macros/src/main/scala/com/avsystem/commons/macros/serialization/MongoMacros.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class MongoMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) {
1414
lazy val MapApplySym: Symbol = typeOf[scala.collection.Map[Any, Any]].member(TermName("apply"))
1515
lazy val TypedMapApplySym: Symbol = getType(tq"$MiscPkg.TypedMap[$ScalaPkg.Any]").member(TermName("apply"))
1616
lazy val AdtAsSym: Symbol = getType(tq"$MongoTypedPkg.AbstractMongoDataCompanion[Any, Any]#macroDslExtensions").member(TermName("as"))
17+
lazy val PolyAdtAsSym: Symbol = getType(tq"$MongoTypedPkg.AbstractMongoPolyDataCompanion[Any, Any]#macroDslExtensions").member(TermName("as"))
1718

1819
// check if some symbol is an abstract method of a sealed trait/class implemented in every case class
1920
// by a field with exactly the same type
@@ -52,7 +53,8 @@ class MongoMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) {
5253
case body: Ident if body.symbol == param.symbol =>
5354
c.prefix.tree
5455

55-
case TypeApply(Select(Apply(_, List(prefix)), TermName("as")), List(subtpe)) if body.symbol == AdtAsSym =>
56+
case TypeApply(Select(Apply(_, List(prefix)), TermName("as")), List(subtpe))
57+
if body.symbol == AdtAsSym || body.symbol == PolyAdtAsSym =>
5658
q"${extractRefStep(prefix)}.as[$subtpe]"
5759

5860
case Select(prefix, name: TermName) =>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.avsystem.commons
2+
package mongo.typed
3+
4+
// MongoPolyDataCompanion is a 2.13 only feature
5+
// This serves only to avoid writing macro code cross-compiled for Scala 2.12
6+
private trait AbstractMongoPolyDataCompanion {
7+
trait macroDslExtensions {
8+
def as[C]: C = sys.error("stub")
9+
}
10+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.avsystem.commons
2+
package mongo.typed
3+
4+
import com.avsystem.commons.annotation.explicitGenerics
5+
import com.avsystem.commons.meta.MacroInstances
6+
import com.avsystem.commons.mongo.BsonGenCodecs
7+
import com.avsystem.commons.serialization.{GenCodec, GenObjectCodec}
8+
9+
import scala.annotation.compileTimeOnly
10+
11+
trait MongoPolyAdtInstances[D[_]] {
12+
// needed by MongoAdtFormat.materialize for generic type
13+
protected final implicit def codecFromFormat[T: MongoFormat]: GenCodec[T] = MongoFormat[T].codec
14+
15+
def codec[T: GenCodec]: GenObjectCodec[D[T]]
16+
def format[T: MongoFormat]: MongoAdtFormat[D[T]]
17+
}
18+
19+
abstract class AbstractMongoPolyDataCompanion[Implicits, D[_]](implicits: Implicits)(
20+
implicit instances: MacroInstances[Implicits, MongoPolyAdtInstances[D]]
21+
) {
22+
implicit def codec[T: GenCodec]: GenObjectCodec[D[T]] = instances(implicits, this).codec[T]
23+
implicit def format[T: MongoFormat]: MongoAdtFormat[D[T]] = instances(implicits, this).format[T]
24+
25+
implicit def isMongoAdtOrSubtype[C <: D[_]]: IsMongoAdtOrSubtype[C] = null
26+
27+
implicit class macroDslExtensions[T](value: D[T]) {
28+
@explicitGenerics
29+
@compileTimeOnly("the .as[Subtype] construct can only be used inside lambda passed to .ref(...) macro")
30+
def as[C <: D[T]]: C = sys.error("stub")
31+
}
32+
33+
@explicitGenerics
34+
def dsl[T: MongoFormat]: DataTypeDsl[D[T]] = new DataTypeDsl[D[T]] {
35+
def SelfRef: MongoRef[D[T], D[T]] = MongoRef.RootRef(format[T])
36+
}
37+
}
38+
39+
/**
40+
* Like [[MongoDataCompanion]] buf for generic types (with exactly one unbounded type parameter).
41+
*
42+
* @example
43+
* {{{
44+
* case class Point[+T](x: T, y: T)
45+
* object Point extends MongoPolyDataCompanion[Point] {
46+
* def XRef[T: MongoFormat]: MongoPropertyRef[Point[T], T] =
47+
* Point.dsl[T].ref(_.x)
48+
* }
49+
* }}}
50+
*/
51+
abstract class MongoPolyDataCompanion[D[_]](
52+
implicit instances: MacroInstances[BsonGenCodecs.type, MongoPolyAdtInstances[D]]
53+
) extends AbstractMongoPolyDataCompanion[BsonGenCodecs.type, D](BsonGenCodecs)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.avsystem.commons
2+
package mongo.typed
3+
4+
import com.avsystem.commons.serialization.flatten
5+
import org.scalatest.funsuite.AnyFunSuite
6+
7+
@flatten sealed trait PolyMongoUnion[+T]
8+
object PolyMongoUnion extends MongoPolyDataCompanion[PolyMongoUnion] {
9+
case class CaseOne[+T](value: T, str: String) extends PolyMongoUnion[T]
10+
case class CaseTwo[+T](foo: T, int: Int) extends PolyMongoUnion[T]
11+
case object CaseThree extends PolyMongoUnion[Nothing]
12+
}
13+
14+
case class PolyMongoRecord[+T](value: T, meta: String)
15+
object PolyMongoRecord extends MongoPolyDataCompanion[PolyMongoRecord]
16+
17+
class MongoPolyDataTest extends AnyFunSuite {
18+
final val Pmr = PolyMongoRecord
19+
final val Pmu = PolyMongoUnion
20+
21+
test("filterPath") {
22+
assert(Pmr.dsl[Int].ref(_.value).rawPath == "value")
23+
assert(Pmu.dsl[Int].ref(_.as[PolyMongoUnion.CaseOne[Int]].value).rawPath == "value")
24+
assert(Pmu.dsl[Int].as[PolyMongoUnion.CaseOne[Int]].ref(_.value).rawPath == "value")
25+
assert(Pmu.dsl[Int].ref(_.as[PolyMongoUnion.CaseOne[Int]].str).rawPath == "str")
26+
assert(Pmu.dsl[PolyMongoUnion[Int]].ref(x =>
27+
x.as[PolyMongoUnion.CaseOne[PolyMongoUnion[Int]]].value
28+
.as[PolyMongoUnion.CaseOne[Int]].str
29+
).rawPath == "value.str")
30+
}
31+
}

commons-redis/src/main/scala/com/avsystem/commons/redis/commands/ReplyDecoders.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,8 @@ object ReplyDecoders {
309309
val multiBulkAsXGroupInfo: ReplyDecoder[XGroupInfo] =
310310
flatMultiBulkAsMap(bulkAsUTF8, undecoded).andThen(XGroupInfo)
311311

312-
def multiBulkAsXStreamInfoOf[Record: RedisRecordCodec]: ReplyDecoder[XStreamInfo[Record]] =
313-
flatMultiBulkAsMap(bulkAsUTF8, undecoded).andThen(XStreamInfo[Record](_))
312+
def multiBulkAsXStreamInfoOf[Rec: RedisRecordCodec]: ReplyDecoder[XStreamInfo[Rec]] =
313+
flatMultiBulkAsMap(bulkAsUTF8, undecoded).andThen(XStreamInfo[Rec](_))
314314

315315
def multiBulkAsGroupedSeq[T](size: Int, elementDecoder: ReplyDecoder[T]): ReplyDecoder[Seq[Seq[T]]] = {
316316
case ArrayMsg(elements) =>

commons-redis/src/main/scala/com/avsystem/commons/redis/commands/streams.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,12 @@ case class XConsumerInfo(raw: BMap[String, ValidRedisMsg]) {
447447
def idle: Long = integerAsLong(raw("idle"))
448448
}
449449

450-
case class XStreamInfo[Record: RedisRecordCodec](raw: BMap[String, ValidRedisMsg]) {
450+
case class XStreamInfo[Rec: RedisRecordCodec](raw: BMap[String, ValidRedisMsg]) {
451451
def length: Long = integerAsLong(raw("length"))
452452
def radixTreeKeys: Int = integerAsInt(raw("radis-tree-keys"))
453453
def radixTreeNodes: Int = integerAsInt(raw("radis-tree-nodes"))
454454
def groups: Int = integerAsInt(raw("groups"))
455455
def lastGeneratedId: XEntryId = bulkAsXEntryId(raw("last-generated-id"))
456-
def firstEntry: XEntry[Record] = multiBulkAsXEntryOf[Record].apply(raw("first-entry"))
457-
def lastEntry: XEntry[Record] = multiBulkAsXEntryOf[Record].apply(raw("last-entry"))
456+
def firstEntry: XEntry[Rec] = multiBulkAsXEntryOf[Rec].apply(raw("first-entry"))
457+
def lastEntry: XEntry[Rec] = multiBulkAsXEntryOf[Rec].apply(raw("last-entry"))
458458
}

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# suppress inspection "UnusedProperty"
2-
sbt.version=1.5.5
2+
sbt.version=1.5.7

0 commit comments

Comments
 (0)