in protobuf/src/main/scala/magnolify/protobuf/ProtobufType.scala [98:183]
def join[T](caseClass: CaseClass[Typeclass, T]): ProtobufField[T] = {
if (caseClass.isValueClass) {
val p = caseClass.parameters.head
val tc = p.typeclass
new ProtobufField[T] {
override type FromT = tc.FromT
override type ToT = tc.ToT
override val default: Option[T] = tc.default.map(x => caseClass.construct(_ => x))
override def from(v: FromT)(cm: CaseMapper): T = caseClass.construct(_ => tc.from(v)(cm))
override def to(v: T, b: Message.Builder)(cm: CaseMapper): ToT =
tc.to(p.dereference(v), b)(cm)
}
} else {
new Record[T] {
// One Record[T] instance may be used for multiple Message types
@transient private lazy val fieldsCache: concurrent.Map[String, Array[FieldDescriptor]] =
concurrent.TrieMap.empty
private def getFields(descriptor: Descriptor)(cm: CaseMapper): Array[FieldDescriptor] =
fieldsCache.getOrElseUpdate(
descriptor.getFullName, {
val fields = new Array[FieldDescriptor](caseClass.parameters.size)
caseClass.parameters.foreach(p =>
fields(p.index) = descriptor.findFieldByName(cm.map(p.label))
)
fields
}
)
private def newFieldBuilder(b: Message.Builder)(f: FieldDescriptor): Message.Builder =
if (f.getType != FieldDescriptor.Type.MESSAGE) null
else b.newBuilderForField(f)
override def checkDefaults(descriptor: Descriptor)(cm: CaseMapper): Unit = {
val fields = getFields(descriptor)(cm)
caseClass.parameters.foreach { p =>
val field = fields(p.index)
val protoDefault = if (field.hasDefaultValue) {
Some(p.typeclass.fromAny(field.getDefaultValue)(cm))
} else {
p.typeclass.default
}
p.default.foreach { d =>
require(
protoDefault.contains(d),
s"Default mismatch ${caseClass.typeName.full}#${p.label}: $d != ${protoDefault.orNull}"
)
}
if (field.getType == FieldDescriptor.Type.MESSAGE) {
p.typeclass.checkDefaults(field.getMessageType)(cm)
}
}
}
override def from(v: Message)(cm: CaseMapper): T = {
val descriptor = v.getDescriptorForType
val fields = getFields(descriptor)(cm)
caseClass.construct { p =>
val field = fields(p.index)
// check hasPresence to make sure hasField is meaningful
val value = if (field.hasPresence && !v.hasField(field)) {
null
} else {
v.getField(field)
}
p.typeclass.fromAny(value)(cm)
}
}
override def to(v: T, bu: Message.Builder)(cm: CaseMapper): Message = {
val fields = getFields(bu.getDescriptorForType)(cm)
caseClass.parameters
.foldLeft(bu) { (b, p) =>
val field = fields(p.index)
val builder = newFieldBuilder(bu)(field)
val value = p.typeclass.to(p.dereference(v), builder)(cm)
if (value == null) b else b.setField(field, value)
}
.build()
}
}
}
}