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 jap.fields.CatsInterop.DefaultValidatedNelVM._
import jap.fields.CirceInterop._
import jap.fields._

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

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

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