sbt-avrohugger
sbt-avrohugger copied to clipboard
Any way to extend a trait from a record type's companion object ?
At the moment when generating a record
(I use a SpecificRecord but I guess is the same for the others) also the companion object of the case class is (correctly) generated. So for something like
protocol TestProtocol {
record Test {
long id;
string text;
}
}
the generated code is
import scala.annotation.switch
case class Test(var id: Long, var text: String) extends org.apache.avro.specific.SpecificRecordBase {
def this() = this(0L, "")
def get(field$: Int): AnyRef = {
(field$: @switch) match {
case 0 => {
id
}.asInstanceOf[AnyRef]
case 1 => {
text
}.asInstanceOf[AnyRef]
case _ => new org.apache.avro.AvroRuntimeException("Bad index")
}
}
def put(field$: Int, value: Any): Unit = {
(field$: @switch) match {
case 0 => this.id = {
value
}.asInstanceOf[Long]
case 1 => this.text = {
value.toString
}.asInstanceOf[String]
case _ => new org.apache.avro.AvroRuntimeException("Bad index")
}
()
}
def getSchema: org.apache.avro.Schema = Test.SCHEMA$
}
object Test {
val SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Test\",\"namespace\":\"com.hbc.product.streams.topology.source\",\"fields\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"text\",\"type\":\"string\"}]}")
}
Is there any way I could specify a trait that would be extended by the companion object ? Something like
object Test extends TestInstances {
final val SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Test\",\"namespace\":\"com.hbc.product.streams.topology.source\",\"fields\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"text\",\"type\":\"string\"}]}")
}
The use case I have is that this way it would be possible to put type class instances in the implicit scope for the generated types avoiding orphans. Thanks.
Nothing like exactly like that currently.
However there's a way to customize how case classes are generated. It uses these models. Maybe there's a way to adapt that to get what you want? Maybe something like an optional field with a default of None
, or maybe with varargs?
Did you have something in mind, maybe like SpecificRecord.defaultTypes.copy(record = ScalaCaseClass("TestInstances")
? Or maybe use a map Map
to be more precise, SpecificRecord.defaultTypes.copy(record = ScalaCaseClass(Map("Test" -> "TestInstances"))
?
The issue I have here is that, when I define a type in idl I would need also to place the typeclass instances for that type in the companion object, so what would help is a way to tell the generator how to place those instances in there. As an example consider I wanted to put the instance for Show[Test]
in the implicit scope. I could place it in TestInstances
as here
trait TestInstances {
implicit final val testShow: Show[Test] = ???
}
Or even create a hierarchy that would give me different priorities like
trait TestInstances extends LowPriorityTestInstances {
implicit final val testShow: Show[Test] = ???
}
sealed trait LowPriorityTestInstances {
/** other low priority instances here **/
}
Anything that works on the way the case class is generated or on the type I'm afraid doesn't work. May be an option could be to exploit the annotations that are already used for language specific features (like namespaces, ordering etc.). Something like
protocol TestProtocol {
@companion-is-a("my.fully.qualified.namespace.TestInstances") or
@companion-extends("my.fully.qualified.namespace.TestInstances")
record Test {
long id;
string text;
}
}
with the care of making SCHEMA$
final in this case and considering that TestInstances will be part of the manually generated code (so it will be there when the compilation is attempted or it will generate a compilation error).
Anyway I will try again with the existing configuration options as you suggest to see if I find a way.
Thanks a lot.