A Scala DSL to convert data to a byte sequence representation
import java.io.{ByteArrayOutputStream, DataOutputStream} | |
import shapeless._ | |
case class Container(dos: DataOutputStream, baos: ByteArrayOutputStream) { | |
def ~[A: ByteSequenceRepr](arg: A) = implicitly[ByteSequenceRepr[A]].toByteSequence(this, arg) | |
def ~| : Array[Byte] = { | |
dos.close() | |
baos.toByteArray | |
} | |
} | |
object Container { | |
def apply() = { | |
val baos = new ByteArrayOutputStream() | |
val dos = new DataOutputStream(baos) | |
new Container(dos, baos) | |
} | |
} | |
trait ByteSequenceRepr[A] { | |
def writer: (Container, A) => Unit | |
def toByteSequence(arg: A): Container = { | |
val baos = new ByteArrayOutputStream() | |
val dos = new DataOutputStream(baos) | |
toByteSequence(Container(dos, baos), arg) | |
} | |
def toByteSequence(container: Container, arg: A): Container = { | |
writer(container, arg) | |
container | |
} | |
} | |
trait LowPriorityDefaultByteSequenceReprImplicits { | |
implicit val ByteToByteSequence: ByteSequenceRepr[Byte] = new ByteSequenceRepr[Byte] { | |
val writer: (Container, Byte) => Unit = _.dos.writeByte(_) | |
} | |
implicit val intToByteSequence: ByteSequenceRepr[Int] = new ByteSequenceRepr[Int] { | |
val writer: (Container, Int) => Unit = _.dos.writeInt(_) | |
} | |
implicit val longToByteSequence: ByteSequenceRepr[Long] = new ByteSequenceRepr[Long] { | |
val writer: (Container, Long) => Unit = _.dos.writeLong(_) | |
} | |
implicit val floatToByteSequence: ByteSequenceRepr[Float] = new ByteSequenceRepr[Float] { | |
val writer: (Container, Float) => Unit = _.dos.writeFloat(_) | |
} | |
implicit val doubleToByteSequence: ByteSequenceRepr[Double] = new ByteSequenceRepr[Double] { | |
val writer: (Container, Double) => Unit = _.dos.writeDouble(_) | |
} | |
implicit val stringToByteSequence: ByteSequenceRepr[String] = new ByteSequenceRepr[String] { | |
val writer: (Container, String) => Unit = _.dos.writeUTF(_) | |
} | |
implicit val booleanToByteSequence: ByteSequenceRepr[Boolean] = new ByteSequenceRepr[Boolean] { | |
val writer: (Container, Boolean) => Unit = _.dos.writeBoolean(_) | |
} | |
implicit def optionToByteSequence[A: ByteSequenceRepr]: ByteSequenceRepr[Option[A]] = | |
new ByteSequenceRepr[Option[A]] { | |
def writer: (Container, Option[A]) => Unit = { (container, opt) => | |
opt.foreach(value => implicitly[ByteSequenceRepr[A]].toByteSequence(container, value)) | |
} | |
} | |
} | |
object ByteSequenceRepr extends LowPriorityDefaultByteSequenceReprImplicits | |
object Implicits { | |
//implicit conversion from Container to byte array with which you do not need to explicitly call ~| to convert | |
//Container instance to byte array. | |
implicit def containerToByteArray(container: Container): Array[Byte] = container.~| | |
implicit class WithTilde[A](val left: A) extends AnyVal { | |
def ~[B](right: B)(implicit seqA: ByteSequenceRepr[A], | |
seqB: ByteSequenceRepr[B]): Container = { | |
val container = seqA.toByteSequence(left) | |
seqB.toByteSequence(container, right) | |
} | |
def ~|(implicit seqA: ByteSequenceRepr[A]): Array[Byte] = seqA.toByteSequence(left) | |
} | |
implicit val HNilByteSequenceRepr: ByteSequenceRepr[HNil] = new ByteSequenceRepr[HNil] { | |
def writer: (Container, HNil) => Unit = (_, _) => () | |
} | |
implicit def HConsByteSequenceRepr[H, T <: HList](implicit bsrH: Lazy[ByteSequenceRepr[H]], | |
bsrT: Lazy[ByteSequenceRepr[T]]): ByteSequenceRepr[H :: T] = { | |
new ByteSequenceRepr[H :: T] { | |
def writer: (Container, H :: T) => Unit = { (container, cons) => | |
cons match { | |
case (h :: HNil) => bsrH.value.toByteSequence(container, h) | |
case (h :: t) => | |
bsrH.value.toByteSequence(container, h) | |
bsrT.value.toByteSequence(container, t) | |
} | |
} | |
} | |
} | |
implicit def GenericByteSequenceRepr[T, R](implicit gen: Generic.Aux[T, R], | |
bsr: ByteSequenceRepr[R]): ByteSequenceRepr[T] = { | |
new ByteSequenceRepr[T] { | |
def writer: (Container, T) => Unit = { (container, cons) => | |
bsr.value.toByteSequence(container, gen.to(cons)) | |
} | |
} | |
} | |
} | |
import Implicits._ | |
object SampleShapeless extends App { | |
case class Orange(hasVitaminC: Boolean, color: String, size: Int) | |
case class Apple(age: Int) | |
val seqRaw: Array[Byte] = true ~ "orange" ~ 10 ~ 20 | |
val seqRich: Array[Byte] = Orange(hasVitaminC = true, "orange", 10) ~ Apple(20) | |
println(s"Equals: ${util.Arrays.equals(seqRaw, seqRich)}") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment