io.hireproof.structure.Schema.scala Maven / Gradle / Ivy
The newest version!
package io.hireproof.structure
import cats.Eval
import cats.data.{Chain, Validated}
import cats.syntax.all._
import io.circe.syntax._
import io.circe.{Decoder, Encoder, Json, JsonObject}
import io.hireproof.screening.validations.parsing
import io.hireproof.screening.{Constraint, Validation, Violation}
sealed abstract class Schema[A] extends Structure[A] {
override type Self[a] <: Schema[a]
def default: Option[A]
def description: Option[String]
def example: Option[A]
def name: Option[String]
def validations: Chain[Validation[_, _]]
final def setDefault(default: Option[A]): Self[A] =
vimapCopy[A](default, description, example, name, validations)(_.valid, identity)
final def withDefault(default: A): Self[A] = setDefault(default.some)
final def withoutDefault: Self[A] = setDefault(none)
final def setDescription(description: Option[String]): Self[A] =
vimapCopy[A](default, description, example, name, validations)(_.valid, identity)
final def withDescription(description: String): Self[A] = setDescription(description.some)
final def withoutDescription: Self[A] = setDescription(none)
final def setName(description: Option[String]): Self[A] =
vimapCopy[A](default, description, example, name, validations)(_.valid, identity)
final def withName(description: String): Self[A] = setName(description.some)
final def withoutName: Self[A] = setName(none)
final def setExample(example: Option[A]): Self[A] =
vimapCopy[A](default, description, example, name, validations)(_.valid, identity)
final def withExample(example: A): Self[A] = setExample(example.some)
final def withoutExample: Self[A] = setExample(none)
final def jsonExample: Option[Json] = example.flatMap(toJson)
final def stringExample: Option[String] = example.flatMap(toString)
final def jsonDefault: Option[Json] = default.flatMap(toJson)
final def stringDefault: Option[String] = default.flatMap(toString)
final override private[structure] def vimap[T](
f: A => Validated[Errors, T],
g: T => A,
validation: Option[Validation[_, _]]
): Self[T] = vimapCopy[T](default, description, example, name, validations ++ Chain.fromOption(validation))(f, g)
protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Self[T]
final protected def copy(
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
): Self[A] = vimapCopy[A](default, description, example, name, validations)(_.valid, identity)
final def fromJson(json: Option[Json]): Validated[Errors, A] = fromJson(json, default)
private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A]
final def fromString(value: Option[String]): Validated[Errors, A] = fromString(value, default)
private[structure] def fromString(value: Option[String], default: Option[A]): Validated[Errors, A]
def toJson(a: A): Option[Json]
def toString(a: A): Option[String]
}
object Schema {
private val Required: Errors = Errors.rootNel(Violation(Constraint.required, Json.Null))
sealed abstract class Value[A](
override val default: Option[A],
override val description: Option[String],
override val example: Option[A],
val name: Option[String],
override val validations: Chain[Validation[_, _]]
) extends Schema[A] {
override type Self[a] <: Schema.Value[a]
final def fromJsonValue(json: Json): Validated[Errors, A] = fromJsonValue(json, default)
private[structure] def fromJsonValue(json: Json, default: Option[A]): Validated[Errors, A]
final def fromStringValue(value: String): Validated[Errors, A] = fromStringValue(value, default)
private[structure] def fromStringValue(value: String, default: Option[A]): Validated[Errors, A]
def toJsonValue(a: A): Json
def toStringValue(a: A): String
}
sealed abstract class Primitive[A](
default: Option[A],
description: Option[String],
example: Option[A],
val format: Option[String],
name: Option[String],
val tpe: Type,
validations: Chain[Validation[_, _]]
) extends Schema.Value[A](default, description, example, name, validations) { self =>
final override type Self[a] = Schema.Primitive[a]
final def setFormat(format: Option[String]): Schema.Primitive[A] = copy(format = format)
final def withFormat(format: String): Schema.Primitive[A] = setFormat(format.some)
final def withoutFormat: Schema.Primitive[A] = setFormat(none)
final def copy(
default: Option[A] = default,
description: Option[String] = description,
example: Option[A] = example,
format: Option[String] = format,
name: Option[String] = name
): Schema.Primitive[A] =
vimapCopy[A](default, description, example, format, name, validations)(_.valid, identity)
override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Primitive[T] =
vimapCopy(default, description, example, format, name, validations)(f, g)
private def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
format: Option[String],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Primitive[T] = new Primitive[T](
default.flatMap(f(_).toOption),
description,
example.flatMap(f(_).toOption),
format,
name,
tpe,
validations
) {
override def fromJsonValue(json: Json, default: Option[T]): Validated[Errors, T] =
self.fromJsonValue(json, default.map(g)).andThen(f)
override def fromStringValue(value: String, default: Option[T]): Validated[Errors, T] =
self.fromStringValue(value, default.map(g)).andThen(f)
override def toJsonValue(gt: T): Json = self.toJsonValue(g(gt))
override def toStringValue(gt: T): String = self.toStringValue(g(gt))
}
final override private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A] =
json
.traverse(fromJsonValue)
.map(_.orElse(default))
.andThen(Validated.fromOption(_, Required))
final override private[structure] def fromString(
value: Option[String],
default: Option[A]
): Validated[Errors, A] =
value.traverse(fromStringValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
final override def toJson(fa: A): Option[Json] = toJsonValue(fa).some
final override def toString(fa: A): Option[String] = toStringValue(fa).some
}
object Primitive {
private def apply[A: Decoder: Encoder](
format: Option[String],
parse: Validation[String, A],
print: A => String,
tpe: Type
): Schema.Primitive[A] = new Primitive[A](
default = none,
description = none,
example = none,
format,
name = none,
tpe,
validations = Chain.empty
) {
override def fromJsonValue(json: Json, default: Option[A]): Validated[Errors, A] =
if (json.isNull) default.toValid(Required)
else json.as[A].toValidated.leftMap(_ => Errors.rootNel(Violation(Constraint.json(this.tpe.toString), json)))
override def fromStringValue(value: String, default: Option[A]): Validated[Errors, A] =
parse.run(value).leftMap(Errors.root)
override def toJsonValue(a: A): Json = a.asJson
override def toStringValue(a: A): String = print(a)
}
val bigDecimal: Schema.Primitive[BigDecimal] = Primitive(none, parsing.bigDecimal, String.valueOf, Type.Number)
val bigInt: Schema.Primitive[BigInt] = Primitive(none, parsing.bigInt, String.valueOf, Type.Number)
val boolean: Schema.Primitive[Boolean] = Primitive(none, parsing.boolean, String.valueOf, Type.Boolean)
val double: Schema.Primitive[Double] = Primitive("double".some, parsing.double, String.valueOf, Type.Number)
val float: Schema.Primitive[Float] = Primitive("float".some, parsing.float, String.valueOf, Type.Number)
val int: Schema.Primitive[Int] = Primitive("int32".some, parsing.int, String.valueOf, Type.Integer)
val long: Schema.Primitive[Long] = Primitive("int64".some, parsing.long, String.valueOf, Type.Integer)
val string: Schema.Primitive[String] = Primitive(none, Validation.ask, identity, Type.String)
}
sealed abstract class Optional[A](
override val default: Option[A],
val schema: Schema[_],
override val example: Option[A]
) extends Schema[A] { self =>
final override type Self[a] = Schema.Optional[a]
final override def description: Option[String] = schema.description
final override def name: Option[String] = schema.name
final override def validations: Chain[Validation[_, _]] = schema.validations
final override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Optional[T] =
new Optional[T](
default.flatMap(f(_).toOption),
schema.copy(default = none, description, example = none, name, validations),
example.flatMap(f(_).toOption)
) {
override def fromJson(json: Option[Json], default: Option[T]): Validated[Errors, T] =
self.fromJson(json, default.map(g)).andThen(f)
override def fromString(value: Option[String], default: Option[T]): Validated[Errors, T] =
self.fromString(value, default.map(g)).andThen(f)
override def toJson(fa: T): Option[Json] = self.toJson(g(fa))
override def toString(fa: T): Option[String] = self.toString(g(fa))
}
}
object Optional {
def apply[A](schema: Schema[A]): Schema.Optional[Option[A]] = {
val s = schema
new Schema.Optional[Option[A]](default = none, s, example = none) {
override def fromJson(json: Option[Json], default: Option[Option[A]]): Validated[Errors, Option[A]] =
json.flatMap(_.as[Option[Json]].toOption.flatten).traverse(json => s.fromJson(json.some))
override def fromString(value: Option[String], default: Option[Option[A]]): Validated[Errors, Option[A]] =
value.traverse(value => s.fromString(value.some))
override def toJson(fa: Option[A]): Option[Json] = fa.flatMap(s.toJson)
override def toString(fa: Option[A]): Option[String] = fa.flatMap(s.toString)
}
}
}
sealed abstract class Collection[A](
default: Option[A],
val delimiter: Delimiter,
description: Option[String],
example: Option[A],
name: Option[String],
val schema: Eval[Schema[_]],
validations: Chain[Validation[_, _]]
) extends Schema.Value[A](default, description, example, name, validations) { self =>
final override type Self[a] = Schema.Collection[a]
final def withDelimiter(delimiter: Delimiter): Schema.Collection[A] = copy(delimiter = delimiter)
final def copy(
default: Option[A] = default,
delimiter: Delimiter = delimiter,
description: Option[String] = description,
example: Option[A] = example,
name: Option[String] = name
): Schema.Collection[A] =
vimapCopy(default, delimiter, description, example, name, validations)(_.valid, identity)
final override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Collection[T] =
vimapCopy(default, delimiter, description, example, name, validations)(f, g)
final private def vimapCopy[T](
default: Option[A],
delimiter: Delimiter,
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Collection[T] = new Collection[T](
default.flatMap(f(_).toOption),
delimiter,
description,
example.flatMap(f(_).toOption),
name,
schema,
validations
) {
override def fromJson(json: Option[Json], default: Option[T]): Validated[Errors, T] =
self.fromJson(json, default.map(g)).andThen(f)
override def fromString(value: Option[String], default: Option[T]): Validated[Errors, T] =
self.fromString(value, default.map(g)).andThen(f)
override def fromJsonValue(json: Json, default: Option[T]): Validated[Errors, T] =
self.fromJsonValue(json, default.map(g)).andThen(f)
override def fromStringValue(value: String, default: Option[T]): Validated[Errors, T] =
self.fromStringValue(value, default.map(g)).andThen(f)
override def toJsonValue(a: T): Json = self.toJsonValue(g(a))
override def toStringValue(a: T): String = self.toStringValue(g(a))
}
final override def toJson(a: A): Option[Json] = toJsonValue(a).some
final override def toString(a: A): Option[String] = toStringValue(a).some
}
object Collection {
def seq[A](schema: => Schema[A]): Schema.Collection[Seq[A]] = {
val s = Eval.later(schema)
new Collection[Seq[A]](
default = none,
delimiter = Delimiter.Default,
description = none,
example = none,
name = none,
s,
validations = Chain.empty
) {
override def fromJson(json: Option[Json], default: Option[Seq[A]]): Validated[Errors, Seq[A]] =
json.traverse(fromJsonValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
override def fromString(value: Option[String], default: Option[Seq[A]]): Validated[Errors, Seq[A]] =
value.traverse(fromStringValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
override def fromJsonValue(json: Json, default: Option[Seq[A]]): Validated[Errors, Seq[A]] =
if (json.isNull) default.toValid(Required)
else
json
.as[Seq[Json]]
.toValidated
.leftMap(_ => Errors.rootNel(Violation(Constraint.json("[]"), json)))
.andThen(_.zipWithIndex.traverse { case (json, index) =>
s.value.fromJson(json.some).leftMap(_.modifyHistory(index /: _))
})
override def fromStringValue(value: String, default: Option[Seq[A]]): Validated[Errors, Seq[A]] =
delimiter.decode(value).toList.traverse(value => s.value.fromString(value.some))
override def toJsonValue(a: Seq[A]): Json = s.value match {
case s: Schema.Value[_] => a.map(s.toJsonValue).asJson
case s: Schema.Optional[_] => a.map(s.toJson).asJson
}
override def toStringValue(a: Seq[A]): String = delimiter.encode(Chain.fromSeq(a).mapFilter(s.value.toString))
}
}
}
sealed abstract class Dictionary[A](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
val schema: Eval[Schema[_]],
validations: Chain[Validation[_, _]]
) extends Schema.Value[A](default, description, example, name, validations) { self =>
final override type Self[a] = Schema.Dictionary[a]
override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Dictionary[T] = new Dictionary[T](
default.flatMap(f(_).toOption),
description,
example.flatMap(f(_).toOption),
name,
schema,
validations
) {
override def fromJsonObject(json: JsonObject, default: Option[T]): Validated[Errors, T] =
self.fromJsonObject(json, default.map(g)).andThen(f)
override def toJsonObject(fa: T): JsonObject = self.toJsonObject(g(fa))
}
final override private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A] =
json.traverse(fromJsonValue).map(_.orElse(default)).andThen {
case Some(a) => a.valid
case None => fromJsonObject(JsonObject.empty, default)
}
final override private[structure] def fromString(
value: Option[String],
default: Option[A]
): Validated[Errors, A] =
value.traverse(fromStringValue).map(_.orElse(default)).andThen {
case Some(a) => a.valid
case None => fromJsonObject(JsonObject.empty, default)
}
final override private[structure] def fromJsonValue(json: Json, default: Option[A]): Validated[Errors, A] =
if (json.isNull) default match {
case Some(a) => a.valid
case None => fromJsonObject(JsonObject.empty, default)
}
else
json.asObject
.toValid(Errors.rootNel(Violation(Constraint.json("{}"), json)))
.andThen(fromJsonObject(_, default))
final override private[structure] def fromStringValue(
value: String,
default: Option[A]
): Validated[Errors, A] =
parsing.json.run(value).leftMap(Errors.root).andThen(fromJsonValue)
final override def toJson(fa: A): Option[Json] = toJsonValue(fa).some
final override def toString(fa: A): Option[String] = toStringValue(fa).some
final override def toJsonValue(a: A): Json = Json.fromJsonObject(toJsonObject(a))
final override def toStringValue(a: A): String = toJsonValue(a).noSpaces
private[structure] def fromJsonObject(json: JsonObject, default: Option[A]): Validated[Errors, A]
private[structure] def toJsonObject(fa: A): JsonObject
}
object Dictionary {
def map[A](schema: => Schema[A]): Schema.Dictionary[Map[String, A]] = {
val s = schema
new Dictionary[Map[String, A]](
default = none,
description = none,
example = none,
name = none,
schema = Eval.later(s),
validations = Chain.empty
) {
override def fromJsonObject(
json: JsonObject,
default: Option[Map[String, A]]
): Validated[Errors, Map[String, A]] = json.toList
.traverse { case (key, json) =>
s.fromJson(json.some).tupleLeft(key).leftMap(_.modifyHistory(key /: _))
}
.map(_.toMap)
override def toJsonObject(fa: Map[String, A]): JsonObject = fa.foldLeft(JsonObject.empty) {
case (json, (key, value)) =>
s.toJson(value).fold(json)(value => (key, value) +: json)
}
}
}
}
sealed abstract class Product[A](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
) extends Schema.Value[A](default, description, example, name, validations)
with Structure.Product[A] { self =>
final override type Self[a] = Schema.Product[a]
final override type Element[a] = Field[a]
final override type Group[a] = Schema.Product[a]
def toChain: Chain[Field[_]]
final override def zipAll[T](schema: Schema.Product[T]): Schema.Product[A |*| T] =
new Product[A |*| T](default = none, description, example = none, name, validations) {
override def toChain: Chain[Field[_]] = self.toChain ++ schema.toChain
override def fromJsonObject(json: JsonObject): Validated[Errors, (JsonObject, A |*| T)] =
self.fromJsonObject(json) match {
case Validated.Valid((json, fa)) => schema.fromJsonObject(json).map(_.map((fa, _)))
case Validated.Invalid(left) =>
schema.fromJsonObject(json) match {
case Validated.Valid(_) => left.invalid
case Validated.Invalid(right) => (left |+| right).invalid
}
}
override def toJsonObject(fa: A |*| T): JsonObject =
self.toJsonObject(fa._1) deepMerge schema.toJsonObject(fa._2)
}
final override def zip[T](field: Field[T]): Schema.Product[A |*| T] = zipAll(Product.fromField(field))
final def copy(
default: Option[A] = default,
description: Option[String] = description,
example: Option[A] = example,
name: Option[String] = name
): Schema.Product[A] = vimapCopy(default, description, example, name, validations)(_.valid, identity)
final override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Product[T] = new Product[T](
default.flatMap(f(_).toOption),
description,
example.flatMap(f(_).toOption),
name,
validations
) {
override def toChain: Chain[Field[_]] = self.toChain
override def fromJsonObject(json: JsonObject): Validated[Errors, (JsonObject, T)] =
self.fromJsonObject(json).andThen(_.traverse(f))
override def toJsonObject(fa: T): JsonObject = self.toJsonObject(g(fa))
}
final override private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A] =
json.traverse(fromJsonValue).map(_.orElse(default)).andThen {
case Some(a) => a.valid
case None => fromJsonObject(JsonObject.empty).map(_._2)
}
final override private[structure] def fromString(
value: Option[String],
default: Option[A]
): Validated[Errors, A] =
value.traverse(fromStringValue).map(_.orElse(default)).andThen {
case Some(a) => a.valid
case None => fromJsonObject(JsonObject.empty).map(_._2)
}
final override private[structure] def fromJsonValue(json: Json, default: Option[A]): Validated[Errors, A] =
if (json.isNull) default match {
case Some(a) => a.valid
case None => fromJsonObject(JsonObject.empty).map(_._2)
}
else
json.asObject
.toValid(Errors.rootNel(Violation(Constraint.json("{}"), json)))
.andThen(fromJsonObject)
.map(_._2)
final override private[structure] def fromStringValue(
value: String,
default: Option[A]
): Validated[Errors, A] =
parsing.json.run(value).leftMap(Errors.root).andThen(fromJsonValue)
final override def toJson(fa: A): Option[Json] = toJsonValue(fa).some
final override def toString(fa: A): Option[String] = toStringValue(fa).some
final override def toJsonValue(fa: A): Json = Json.fromJsonObject(toJsonObject(fa))
final override def toStringValue(fa: A): String = toJsonValue(fa).noSpaces
private[structure] def fromJsonObject(json: JsonObject): Validated[Errors, (JsonObject, A)]
private[structure] def toJsonObject(fa: A): JsonObject
}
object Product {
val Empty: Schema.Product[Unit] = new Product[Unit](
default = none,
description = none,
example = none,
name = none,
validations = Chain.empty
) {
override def toChain: Chain[Field[_]] = Chain.empty
override def fromJsonObject(json: JsonObject): Validated[Errors, (JsonObject, Unit)] = (json, ()).valid
override def toJsonObject(fa: Unit): JsonObject = JsonObject.empty
}
def fromField[A](field: Field[A]): Schema.Product[A] =
new Product[A](default = none, description = none, example = none, name = none, validations = Chain.empty) {
override def toChain: Chain[Field[_]] = Chain.one(field)
override def fromJsonObject(json: JsonObject): Validated[Errors, (JsonObject, A)] =
field.fromJsonObject(json)
override def toJsonObject(a: A): JsonObject = field.toJsonObject(a)
}
}
sealed abstract class Sum[A](
default: Option[A],
description: Option[String],
val discriminator: Option[Discriminator],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
) extends Schema.Value[A](default, description, example, name, validations)
with Structure.Sum[A] { self =>
final override type Self[a] = Sum[a]
final override type Element[a] = Branch[a]
final override type Group[a] = Sum[a]
def toChain: Chain[Branch[_]]
final def setDiscriminator(discriminator: Option[Discriminator]): Schema.Sum[A] =
copy(discriminator = discriminator)
final def withDiscriminator(discriminator: Discriminator): Schema.Sum[A] = setDiscriminator(discriminator.some)
final def withoutDiscriminator: Schema.Sum[A] = setDiscriminator(none)
final def copy(
default: Option[A] = default,
description: Option[String] = description,
discriminator: Option[Discriminator] = discriminator,
example: Option[A] = example,
name: Option[String] = name
): Schema.Sum[A] = vimapCopy(default, description, example, name, validations)(_.valid, identity)
final override def orElseAll[T](schema: Schema.Sum[T]): Schema.Sum[A |+| T] = new Sum[A |+| T](
default = none,
description,
discriminator,
example = none,
name,
validations
) {
override def toChain: Chain[Branch[_]] = self.toChain ++ schema.toChain
override def fromJson(
discriminator: Option[Discriminator],
json: Option[Json],
default: Option[A |+| T]
): Validated[Errors, Option[A |+| T]] =
self.fromJson(discriminator, json, default.flatMap(_.left.toOption)).andThen {
case Some(fa) => fa.asLeft.some.valid
case None => schema.fromJson(discriminator, json, default.flatMap(_.toOption)).map(_.map(_.asRight))
}
override def toJson(discriminator: Option[Discriminator], fa: A |+| T): Option[Json] = fa match {
case Left(fa) => self.toJson(discriminator, fa)
case Right(gt) => schema.toJson(discriminator, gt)
}
}
final override def orElse[T](branch: Branch[T]): Schema.Sum[A |+| T] = orElseAll(Sum.fromBranch(branch))
final override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Sum[T] = new Sum[T](
default.flatMap(f(_).toOption),
description,
discriminator,
example.flatMap(f(_).toOption),
name,
validations
) {
override def toChain: Chain[Branch[_]] = self.toChain
override def fromJson(
discriminator: Option[Discriminator],
json: Option[Json],
default: Option[T]
): Validated[Errors, Option[T]] = self.fromJson(discriminator, json, default.map(g)).andThen(_.traverse(f))
override def toJson(discriminator: Option[Discriminator], gt: T): Option[Json] =
self.toJson(discriminator, g(gt))
}
final override private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A] =
json match {
case Some(json) =>
fromJson(discriminator, json.some, default)
.andThen(_.toValid(Errors.rootNel(Violation.invalid(json))))
case None => default.fold(fromJson(discriminator, json, default).andThen(_.toValid(Required)))(_.valid)
}
final override private[structure] def fromJsonValue(json: Json, default: Option[A]): Validated[Errors, A] =
fromJson(json.some)
final override private[structure] def fromString(
value: Option[String],
default: Option[A]
): Validated[Errors, A] =
value.traverse(parsing.json.run).leftMap(Errors.root).andThen(fromJson)
final override private[structure] def fromStringValue(
value: String,
default: Option[A]
): Validated[Errors, A] = fromString(value.some)
final override def toJson(fa: A): Option[Json] = toJsonValue(fa).some
final override def toString(fa: A): Option[String] = toStringValue(fa).some
final override def toJsonValue(fa: A): Json = toJson(discriminator, fa).getOrElse(Json.Null)
final override def toStringValue(fa: A): String = toJsonValue(fa).noSpaces
protected def fromJson(
discriminator: Option[Discriminator],
json: Option[Json],
default: Option[A]
): Validated[Errors, Option[A]]
protected def toJson(discriminator: Option[Discriminator], fa: A): Option[Json]
}
object Sum {
def fromBranch[A](branch: Branch[A]): Schema.Sum[A] = new Sum[A](
default = none,
description = none,
Discriminator.Default.some,
example = none,
name = none,
validations = Chain.empty
) {
override def toChain: Chain[Branch[_]] = Chain.one(branch)
override def fromJson(
discriminator: Option[Discriminator],
json: Option[Json],
default: Option[A]
): Validated[Errors, Option[A]] =
branch.fromJson(discriminator, json)
override def toJson(discriminator: Option[Discriminator], a: A): Option[Json] = branch.toJson(discriminator, a)
}
}
sealed abstract class Enumeration[A](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]],
val tpe: Type
) extends Schema.Value[A](default, description, example, name, validations) { self =>
final override type Self[a] = Schema.Enumeration[a]
def stringValues: Set[String]
def jsonValues: Set[Json]
final def copy(
default: Option[A] = default,
description: Option[String] = description,
example: Option[A] = example,
name: Option[String] = name
): Schema.Enumeration[A] = vimapCopy(default, description, example, name, validations)(_.valid, identity)
final override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Enumeration[T] = new Enumeration[T](
default.flatMap(f(_).toOption),
description,
example.flatMap(f(_).toOption),
name,
validations,
tpe
) {
override def stringValues: Set[String] = self.stringValues
override def jsonValues: Set[Json] = self.jsonValues
override def fromJsonValue(json: Json, default: Option[T]): Validated[Errors, T] =
self.fromJsonValue(json, default.map(g)).andThen(f)
override def fromStringValue(value: String, default: Option[T]): Validated[Errors, T] =
self.fromStringValue(value, default.map(g)).andThen(f)
override def toJsonValue(gt: T): Json = self.toJsonValue(g(gt))
override def toStringValue(gt: T): String = self.toStringValue(g(gt))
}
final override private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A] =
json.traverse(fromJsonValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
final override private[structure] def fromString(
value: Option[String],
default: Option[A]
): Validated[Errors, A] =
value.traverse(fromStringValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
final override def toJson(fa: A): Option[Json] = toJsonValue(fa).some
final override def toString(fa: A): Option[String] = toStringValue(fa).some
}
object Enumeration {
def apply[A, B](schema: Schema.Primitive[A], values: Set[B], mapping: B => A): Schema.Enumeration[B] =
new Enumeration[B](
default = none,
description = none,
example = none,
name = none,
validations = Chain.empty,
tpe = schema.tpe
) {
val inverse: Map[A, B] = values.toList.map(b => (mapping(b), b)).toMap
override def stringValues: Set[String] = values.map(mapping).map(schema.toStringValue)
override def jsonValues: Set[Json] = values.map(mapping).map(schema.toJsonValue)
override def fromJsonValue(json: Json, default: Option[B]): Validated[Errors, B] =
if (json.isNull) default.toValid(Required)
else schema.fromJsonValue(json, default.map(mapping.apply)).map(inverse)
override def fromStringValue(value: String, default: Option[B]): Validated[Errors, B] =
schema.fromStringValue(value, default.map(mapping.apply)).map(inverse)
override def toJsonValue(b: B): Json = schema.toJsonValue(mapping(b))
override def toStringValue(b: B): String = schema.toStringValue(mapping(b))
}
}
sealed abstract class Any[A](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
) extends Schema.Value[A](default, description, example, name, validations) { self =>
override type Self[a] = Schema.Any[a]
final def copy(
default: Option[A] = default,
description: Option[String] = description,
example: Option[A] = example,
name: Option[String] = name
): Schema.Any[A] = vimapCopy(default, description, example, name, validations)(_.valid, identity)
final override protected def vimapCopy[T](
default: Option[A],
description: Option[String],
example: Option[A],
name: Option[String],
validations: Chain[Validation[_, _]]
)(f: A => Validated[Errors, T], g: T => A): Schema.Any[T] =
new Any[T](default.flatMap(f(_).toOption), description, example.flatMap(f(_).toOption), name, validations) {
override def fromJsonValue(json: Json, default: Option[T]): Validated[Errors, T] =
self.fromJsonValue(json, default.map(g)).andThen(f)
override def fromStringValue(value: String, default: Option[T]): Validated[Errors, T] =
self.fromStringValue(value, default.map(g)).andThen(f)
override def toJsonValue(a: T): Json = self.toJsonValue(g(a))
override def toStringValue(a: T): String = self.toStringValue(g(a))
}
final override private[structure] def fromJson(json: Option[Json], default: Option[A]): Validated[Errors, A] =
json.traverse(fromJsonValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
final override private[structure] def fromString(
value: Option[String],
default: Option[A]
): Validated[Errors, A] =
value.traverse(fromStringValue).map(_.orElse(default)).andThen(Validated.fromOption(_, Required))
final override def toJson(fa: A): Option[Json] = toJsonValue(fa).some
final override def toString(fa: A): Option[String] = toStringValue(fa).some
}
object Any {
def apply[A](
decodeJson: Json => Validated[Errors, A],
decodeString: String => Validated[Errors, A]
)(encodeJson: A => Json, encodeString: A => String): Schema.Any[A] =
new Any[A](default = none, description = none, example = none, name = none, validations = Chain.empty) {
override def fromJsonValue(json: Json, default: Option[A]): Validated[Errors, A] =
if (json.isNull) default.fold(decodeJson(json))(_.valid) else decodeJson(json)
override def fromStringValue(value: String, default: Option[A]): Validated[Errors, A] = decodeString(value)
override def toJsonValue(a: A): Json = encodeJson(a)
override def toStringValue(a: A): String = encodeString(a)
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy