Skip to content

Commit 55a1730

Browse files
committed
Close #369 - [refined4s-circe] Add KeyEncoder and KeyDecoder to refined4s.modules.circe.derivation.generic.auto
1 parent bf2fae1 commit 55a1730

File tree

5 files changed

+1349
-43
lines changed

5 files changed

+1349
-43
lines changed

modules/refined4s-circe/shared/src/main/scala/refined4s/modules/circe/derivation/generic/auto.scala

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package refined4s.modules.circe.derivation.generic
22

3-
import io.circe.{Decoder, Encoder}
3+
import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder}
44
import refined4s.modules.cats.syntax.contraCoercible
55
import refined4s.{Coercible, RefinedCtor}
66

@@ -17,5 +17,34 @@ trait auto {
1717

1818
inline given derivedRefinedDecoder[A, B](using refinedCtor: RefinedCtor[B, A], decoder: Decoder[A]): Decoder[B] =
1919
decoder.emap(refinedCtor.create)
20+
21+
inline given derivedKeyEncoder[A, B](using coercible: Coercible[A, B], encoder: KeyEncoder[B]): KeyEncoder[A] =
22+
contraCoercible(encoder)
23+
24+
inline given derivedNewtypeKeyDecoder[A, B](using coercible: Coercible[A, B], decoder: KeyDecoder[A]): KeyDecoder[B] =
25+
Coercible.unsafeWrapTC(decoder)
26+
27+
inline given derivedRefinedKeyDecoder[A, B](using refinedCtor: RefinedCtor[B, A], decoder: KeyDecoder[A]): KeyDecoder[B] with {
28+
override def apply(key: String): Option[B] =
29+
decoder.apply(key).flatMap(refinedCtor.create(_).toOption)
30+
}
31+
32+
/** Without this, Circe's decode uses an incorrect Decoder for the value.
33+
* @example
34+
* {{{
35+
* // when
36+
* decode[Map[PortNumber, Int]]("""{ "0": 65536 }""")
37+
*
38+
* // The expected result is
39+
* Map(PortNumber(0), 65536)
40+
*
41+
* // But the actual result is
42+
* // Either[DecodingFailure, Map[PortNumber, Int]] =
43+
* // Left(DecodingFailure(Invalid value: [65536]. It has to be Int between 0 and 65535 (0 <= PortNumber <= 65535), List(DownField(0))))
44+
* // The KeyDecoder is fine here, but the value Decoder `decode` uses is not Decoder[Int] but Decoder[PortNumber].
45+
* }}}
46+
*/
47+
inline given derivedMapDecoder[A, B](using KeyDecoder[A], Decoder[B]): Decoder[Map[A, B]] = io.circe.Decoder.decodeMap
48+
2049
}
2150
object auto extends auto

0 commit comments

Comments
 (0)