Skip to content

Commit 2e32ae0

Browse files
committed
allow nullable in marshalled classes, for people who like to shoot themselves in the foot
1 parent 8141ba8 commit 2e32ae0

File tree

3 files changed

+27
-6
lines changed

3 files changed

+27
-6
lines changed

gremlin-scala/src/test/scala/gremlin/scala/marshallable/MarshallableSpec.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import gremlin.scala.{
66
asScalaVertex,
77
id,
88
label,
9+
nullable,
910
underlying,
1011
Edge,
1112
Element,
@@ -26,6 +27,7 @@ case class CCWithOption(i: Int, s: Option[String])
2627
case class CCWithOptionValueClass(s: String, i: Option[MyValueClass])
2728
case class CCWithOptionAnyVal(x: Option[Int], y: Option[Long])
2829
case class CCWithList(s: String, ss: List[String], is: List[Int], ds: List[Double])
30+
case class CCWithNullable(i: Int, @nullable maybeNull: String)
2931

3032
case class CCWithOptionId(s: String, @id id: Option[Int])
3133
case class CCWithOptionIdNested(s: String, @id id: Option[Int], i: MyValueClass)
@@ -151,6 +153,13 @@ class MarshallableSpec extends WordSpec with Matchers {
151153
properties.map(_.value) shouldBe List("one", "two")
152154
}
153155

156+
"allows members to be `null` if annotated with `@nullable`" in new Fixture {
157+
val cc = CCWithNullable(i = 42, maybeNull = null)
158+
159+
val v = graph + cc
160+
v.toCC[CCWithNullable] shouldBe cc
161+
}
162+
154163
"define their custom marshaller" in new Fixture {
155164
val ccWithOptionNone = CCWithOption(Int.MaxValue, None)
156165

macros/src/main/scala/gremlin/scala/Annotations.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ class underlying extends StaticAnnotation
1414
@getter @beanGetter
1515
class id extends StaticAnnotation
1616

17+
/** annotate a non-option member with `@nullable` to allow `null` values.
18+
* by default this would throw an error on deserialization
19+
* a.k.a. license to shoot yourself in the foot */
20+
@getter @beanGetter
21+
class nullable extends StaticAnnotation
22+
1723
class label(val label: String = "") extends StaticAnnotation
1824

1925
object Annotations {

macros/src/main/scala/gremlin/scala/Marshallable.scala

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ object Marshallable {
3232
val decoded = name.decodedName.toString
3333
val returnType = field.returnType
3434

35-
def handleStandardProperty = {
35+
def handleStandardProperty(nullable: Boolean) = {
3636
// check if the property is a value class and try to extract everything we need to unwrap it
3737
val treesForValueClass = for {
3838
valueName <- valueGetter(returnType)
@@ -45,9 +45,16 @@ object Marshallable {
4545
_toCCParams :+ q"$valueClassCompanion(element.value[$wrappedType]($decoded)).asInstanceOf[$returnType]")
4646
}
4747
treesForValueClass.getOrElse { //normal property
48+
val toCCParams =
49+
if (!nullable) {
50+
q"element.value[$returnType]($decoded)"
51+
} else {
52+
// for people who like to shoot themselves in the foot
53+
q"new _root_.gremlin.scala.PropertyOps(element.property[$returnType]($decoded)).toOption.orNull"
54+
}
4855
(_idParam,
4956
_fromCCParams :+ q"_root_.scala.collection.immutable.List($decoded -> cc.$name)",
50-
_toCCParams :+ q"element.value[$returnType]($decoded)")
57+
_toCCParams :+ toCCParams)
5158
}
5259
}
5360

@@ -171,7 +178,8 @@ object Marshallable {
171178
} else if (returnType.typeSymbol == weakTypeOf[List[_]].typeSymbol) {
172179
handleListProperty
173180
} else {
174-
handleStandardProperty
181+
handleStandardProperty(
182+
nullable = field.annotations.map(_.tree.tpe).contains(weakTypeOf[nullable]))
175183
}
176184
}
177185

@@ -204,9 +212,7 @@ object Marshallable {
204212
}
205213
"""
206214
}
207-
// if (tpe.toString.contains("CCWithList")) {
208-
// println(ret)
209-
// }
215+
// if (tpe.toString.contains("CCWithNullable")) println(ret)
210216
ret
211217
}
212218

0 commit comments

Comments
 (0)