io.hireproof.structure.Branch.scala Maven / Gradle / Ivy
The newest version!
package io.hireproof.structure
import cats.data.Validated
import cats.syntax.all._
import cats.{Eval, Invariant}
import io.circe.Json
import io.circe.syntax._
import io.hireproof.screening.{Constraint, Validation, Violation}
final case class Branch[A](name: String, schema: Eval[Schema[A]]) extends Structure.Sum[A] {
override type Self[a] = Branch[a]
override type Element[a] = Branch[a]
override type Group[a] = Schema.Sum[a]
override def orElseAll[T](schema: Schema.Sum[T]): Schema.Sum[A |+| T] = toSum.orElseAll(schema)
override def orElse[T](branch: Branch[T]): Schema.Sum[A |+| T] = toSum.orElse(branch)
def toSum: Schema.Sum[A] = Schema.Sum.fromBranch(this)
override private[structure] def vimap[T](
f: A => Validated[Errors, T],
g: T => A,
validation: Option[Validation[_, _]]
): Branch[T] = copy(schema = schema.map(_.vimap(f, g, validation)))
def fromJson(discriminator: Option[Discriminator], json: Option[Json]): Validated[Errors, Option[A]] =
discriminator match {
case Some(Discriminator.Nested(identifier, value)) =>
json
.toValid(Errors.rootNel(Violation(Constraint.required, json)))
.map(_.hcursor)
.andThen { cursor =>
cursor
.get[Option[Json]](identifier)
.toValidated
.leftMap(Errors.fromThrowable)
.andThen(_.toValid(Errors.rootNel(Violation(Constraint.required, Json.Null))))
.andThen { json =>
json
.as[String]
.toValidated
.leftMap(_ => Errors.rootNel(Violation(Constraint.json("string"), json)))
}
.leftMap(_.modifyHistory(identifier /: _))
.andThen { identifier =>
if (name === identifier)
cursor
.get[Option[Json]](value)
.toValidated
.leftMap(Errors.fromThrowable)
.andThen(schema.value.fromJson)
.leftMap(_.modifyHistory(value /: _))
.map(_.some)
else none[A].valid
}
}
case Some(Discriminator.Merged(identifier)) =>
json
.toValid(Errors.rootNel(Violation(Constraint.required, json)))
.map(_.hcursor)
.andThen { cursor =>
cursor
.get[Option[Json]](identifier)
.toValidated
.leftMap(Errors.fromThrowable)
.andThen(_.toValid(Errors.rootNel(Violation(Constraint.required, Json.Null))))
.andThen { json =>
json
.as[String]
.toValidated
.leftMap(_ => Errors.rootNel(Violation(Constraint.json("string"), json)))
}
.leftMap(_.modifyHistory(identifier /: _))
.andThen { identifier =>
if (name === identifier) schema.value.fromJson(json).map(_.some) else none[A].valid
}
}
case None => schema.value.fromJson(json).toOption.valid
}
def toJson(discriminator: Option[Discriminator], a: A): Option[Json] = discriminator match {
case Some(Discriminator.Nested(identifier, value)) =>
Json.obj(identifier := name, value := schema.value.toJson(a).getOrElse(Json.Null)).dropNullValues.some
case Some(Discriminator.Merged(identifier)) =>
(schema.value.toJson(a).getOrElse(Json.Null) deepMerge Json.obj(identifier := name)).some
case None => schema.value.toJson(a)
}
}
object Branch {
def default[A](name: String, schema: => Schema[A]): Branch[A] = Branch(name, Eval.later(schema))
implicit val invariant: Invariant[Branch] = new Invariant[Branch] {
override def imap[A, B](fa: Branch[A])(f: A => B)(g: B => A): Branch[B] = fa.imap(f)(g)
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy