package tf.bug.starry import scodec.bits.ByteVector import shapeless.{::, HList, HNil, ProductTypeClass, ProductTypeClassCompanion} import spire.math.UByte import tf.bug.nose.{Color, ColorFormat} trait Bytable[T] { def apply(t: T): ByteVector } object Bytable extends BytableImplicits trait BytableImplicits extends ProductTypeClassCompanion[Bytable] { implicit class BytableOps[T](t: T)(implicit ev: Bytable[T]) { def asBytes: ByteVector = ev(t) } implicit def messageAsBytable[P, M <: Message[P]]: Bytable[M] = new Bytable[M] { override def apply(t: M): ByteVector = { import t.payloadEv t.length.asBytes ++ t.id.asBytes ++ t.payload.asBytes } } implicit def stringAsBytable: Bytable[String] = new Bytable[String] { override def apply(t: String): ByteVector = { ByteVector(t.getBytes("UTF-8")) } } implicit def uByteAsBytable: Bytable[UByte] = new Bytable[UByte] { override def apply(t: UByte): ByteVector = ByteVector.fromByte(t.signed) } implicit def intAsBytable: Bytable[Int] = new Bytable[Int] { override def apply(t: Int): ByteVector = ByteVector.fromInt(t) } implicit def listAsBytable[A: Bytable]: Bytable[List[A]] = new Bytable[List[A]] { override def apply(t: List[A]): ByteVector = t.map(_.asBytes).foldLeft(ByteVector.empty)(_ ++ _) } implicit def colorAsBytable: Bytable[Color] = new Bytable[Color] { override def apply(t: Color): ByteVector = { val f = t.rgb List(f.red, f.green, f.blue).map(d => UByte((d * 255).toInt)).asBytes } } object typeClass extends ProductTypeClass[Bytable] { override def product[H, T <: HList](ch: Bytable[H], ct: Bytable[T]): Bytable[H :: T] = new Bytable[H :: T] { override def apply(t: H :: T): ByteVector = ch(t.head) ++ ct(t.tail) } override def emptyProduct: Bytable[HNil] = new Bytable[HNil] { override def apply(t: HNil): ByteVector = ByteVector.empty } override def project[F, G](instance: => Bytable[G], to: F => G, from: G => F): Bytable[F] = new Bytable[F] { override def apply(t: F): ByteVector = instance(to(t)) } } }