Skip to content

Instantly share code, notes, and snippets.

@OndrejSpanel
Created April 8, 2024 10:37
Show Gist options
  • Save OndrejSpanel/bcae5a077fb7a72f232e12e828027aac to your computer and use it in GitHub Desktop.
Save OndrejSpanel/bcae5a077fb7a72f232e12e828027aac to your computer and use it in GitHub Desktop.
Use non-string types as Borer Json key
import io.bullet.borer._
object BorerAnyKey {
trait MapKeyCodec[K] {
def keyToString(k: K): String
def keyFromString(s: String): K
}
implicit object MapKeyCodecString extends MapKeyCodec[String] {
override def keyToString(k: String): String = k
override def keyFromString(s: String): String = s
}
implicit object MapKeyCodecDouble extends MapKeyCodec[Double] {
override def keyToString(k: Double): String = k.toString
override def keyFromString(s: String): Double = s.toDouble
}
implicit object MapKeyCodecLong extends MapKeyCodec[Long] {
override def keyToString(k: Long): String = k.toString
override def keyFromString(s: String): Long = s.toLong
}
implicit object MapKeyCodecInt extends MapKeyCodec[Int] {
override def keyToString(k: Int): String = k.toString
override def keyFromString(s: String): Int = s.toInt
}
implicit object MapKeyCodecIntTuple extends MapKeyCodec[(Int, Int)] {
override def keyToString(k: (Int, Int)): String = {
s"${k._1},${k._2}"
}
override def keyFromString(s: String): (Int, Int) = {
val RegEx = "(.*),(.*)".r
s match {
case RegEx(aString, bString) =>
(aString.toInt, bString.toInt)
}
}
}
implicit def MapKeyCodecTuple[A: MapKeyCodec, B: MapKeyCodec]: MapKeyCodec[(A, B)] = new MapKeyCodec[(A, B)] {
override def keyToString(k: (A, B)): String = {
val aString = implicitly[MapKeyCodec[A]].keyToString(k._1)
val bString = implicitly[MapKeyCodec[A]].keyToString(k._1)
aString + "->" + bString
}
override def keyFromString(s: String): (A, B) = {
val RegEx = "(.*)->(.*)".r
s match {
case RegEx(aString, bString) =>
val a = implicitly[MapKeyCodec[A]].keyFromString(aString)
val b = implicitly[MapKeyCodec[B]].keyFromString(bString)
(a, b)
}
}
}
implicit def mapEncoder[K: MapKeyCodec: Encoder, T: Encoder]: Encoder[Map[K, T]] = Encoder { (w, value) =>
if (w.writingCbor) { // any key type is fine on Cbor
Encoder.forMap[K, T, Map].write(w, value)
} else { // on Json we need string keys only
val k = implicitly[MapKeyCodec[K]]
val valueMap = value.map(kv => k.keyToString(kv._1) -> kv._2)
Encoder.forMap[String, T, Map].write(w, valueMap)
}
}
implicit def mapDecoder[K: MapKeyCodec : Ordering : Decoder, T: Decoder]: Decoder[Map[K, T]] = Decoder { r =>
if (r.readingCbor) {
Decoder.forMap[K, T].read(r)
} else {
val k = implicitly[MapKeyCodec[K]]
val stringMap = Decoder.forMap[String, T].read(r)
stringMap.map(kv => k.keyFromString(kv._1) -> kv._2)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment