Skip to main content

Circe Interop

Fields provides interop with circe so you can add validation step to Decoder. It is added as optional dependency to fields-core module, so if you add circe to your dependencies you then can just import all from CirceInterop and now you can use usePolicy extension method to add validation to you Decoder or Codec.

Note that this is only limited to F[_] that are Sync like Effect.Sync or cats Eval. To support other F[_] implement RunSync for it. Also path is extracted using HasFieldPath type class, so when using custom errors do not forget to implement it, too.

import io.circe._
import fields.circe.interop.*
import fields.value.FieldsDsl.default.*

case class Request(name: String)
object Request {
implicit val policy: Policy[Request] =
Policy[Request]
.subRule(_.name)(
_.minSize(4),
_.assert(_ != "null", _.failMessage("Cannot be null")),
)

implicit val decoder: Decoder[Request] = Decoder.forProduct1("name")(Request.apply).usePolicy(policy.contramap(Field(_)))
}

val json = Json.obj("name" -> Json.fromString("Rag"))
// json: Json = JObject(object[name -> "Rag"])
Request.decoder.decodeAccumulating(json.hcursor)
// res0: Validated[NonEmptyList[DecodingFailure], Request] = Invalid(
// NonEmptyList(
// head = DecodingFailure at .name: .name -> must have minimum size of 4,
// tail = List()
// )
// )