Skip to content

Commit

Permalink
Close #369 - [refined4s-circe] Add KeyEncoder and KeyDecoder to…
Browse files Browse the repository at this point in the history
… `refined4s.modules.circe.derivation.generic.auto`
  • Loading branch information
kevin-lee committed Aug 31, 2024
1 parent bf2fae1 commit 55a1730
Show file tree
Hide file tree
Showing 5 changed files with 1,349 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package refined4s.modules.circe.derivation.generic

import io.circe.{Decoder, Encoder}
import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder}
import refined4s.modules.cats.syntax.contraCoercible
import refined4s.{Coercible, RefinedCtor}

Expand All @@ -17,5 +17,34 @@ trait auto {

inline given derivedRefinedDecoder[A, B](using refinedCtor: RefinedCtor[B, A], decoder: Decoder[A]): Decoder[B] =
decoder.emap(refinedCtor.create)

inline given derivedKeyEncoder[A, B](using coercible: Coercible[A, B], encoder: KeyEncoder[B]): KeyEncoder[A] =
contraCoercible(encoder)

inline given derivedNewtypeKeyDecoder[A, B](using coercible: Coercible[A, B], decoder: KeyDecoder[A]): KeyDecoder[B] =
Coercible.unsafeWrapTC(decoder)

inline given derivedRefinedKeyDecoder[A, B](using refinedCtor: RefinedCtor[B, A], decoder: KeyDecoder[A]): KeyDecoder[B] with {
override def apply(key: String): Option[B] =
decoder.apply(key).flatMap(refinedCtor.create(_).toOption)
}

/** Without this, Circe's decode uses an incorrect Decoder for the value.
* @example
* {{{
* // when
* decode[Map[PortNumber, Int]]("""{ "0": 65536 }""")
*
* // The expected result is
* Map(PortNumber(0), 65536)
*
* // But the actual result is
* // Either[DecodingFailure, Map[PortNumber, Int]] =
* // Left(DecodingFailure(Invalid value: [65536]. It has to be Int between 0 and 65535 (0 <= PortNumber <= 65535), List(DownField(0))))
* // The KeyDecoder is fine here, but the value Decoder `decode` uses is not Decoder[Int] but Decoder[PortNumber].
* }}}
*/
inline given derivedMapDecoder[A, B](using KeyDecoder[A], Decoder[B]): Decoder[Map[A, B]] = io.circe.Decoder.decodeMap

}
object auto extends auto
Loading

0 comments on commit 55a1730

Please sign in to comment.