Skip to content
This repository was archived by the owner on Jan 19, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 39 additions & 32 deletions core/src/main/scala/pear/form/Definition.scala
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
package pear
package form

import scala.language.higherKinds
import scala.language.implicitConversions
import matryoshka.{CorecursiveT, Delay}
import scalaz.{Applicative, Cord, Functor, Kleisli, Show, Traverse, \/}

import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import scala.language.{higherKinds, implicitConversions}

import matryoshka.{CorecursiveT, Delay}
import scalaz.{Applicative, Cord, Functor, Show, Traverse}

object Definition {

type Form = matryoshka.data.Fix[FormF]

sealed trait FormF[A] extends Product with Serializable

sealed trait FormF[A] extends Product with Serializable
final case class Empty[A]() extends FormF[A]
final case class Optional[A](form: A) extends FormF[A]
final case class Fields[A](fields: Vector[(String, A)]) extends FormF[A]
final case class Choice[A](alternatives: Vector[(String, A)]) extends FormF[A]
final case class Sequence[A](element: A) extends FormF[A]
final case class Value[A](constraint: Constraint) extends FormF[A]
final case class Sequence[A](element: Vector[A]) extends FormF[A]
final case class Number[A]() extends FormF[A]
final case class IsoDateTime[A]() extends FormF[A]
final case class MinLength[A](minLength: Int) extends FormF[A]
final case class MaxLength[A](maxLength: Int) extends FormF[A]
final case class Min[A](min: BigDecimal) extends FormF[A]
final case class Max[A](max: BigDecimal) extends FormF[A]
final case class After[A](start: ZonedDateTime) extends FormF[A]
final case class Before[A](end: ZonedDateTime) extends FormF[A]
final case class AndThen[A](lhs: A, rhs: A) extends FormF[A]

implicit def formFTraverse[L]: Traverse[FormF[?]] = new Traverse[FormF[?]] {
implicit def formFTraverse[L]: Traverse[FormF] = new Traverse[FormF] {
import scalaz.std.vector.vectorInstance
import scalaz.syntax.traverse._

def traverseImpl[G[_], A, B](fa: FormF[A])(f: A => G[B])(implicit G: Applicative[G]): G[FormF[B]] = fa match {
case Optional(v) => Functor[G].map(f(v))(Optional.apply)
case Empty() => G.point(Empty[B]())
case Optional(v) => G.map(f(v))(Optional.apply)
case Fields(fs) =>
val (names, values) = fs.unzip
Functor[G].map(values traverse f)(vs => Fields(names zip vs))
G.map(values traverse f)(vs => Fields(names zip vs))
case Choice(alts) =>
val (names, values) = alts.unzip
Functor[G].map(values traverse f)(as => Choice(names zip as))
case Sequence(elem) =>
Functor[G].map(f(elem))(Sequence.apply)
case Value(c) => Applicative[G].point(Value[B](c))
G.map(values traverse f)(as => Choice(names zip as))
case Sequence(elems) => G.map(elems traverse f)(Sequence.apply)
case Number() => G.point(Number[B]())
case IsoDateTime() => G.point(IsoDateTime[B]())
case MinLength(min) => G.point(MinLength[B](min))
case MaxLength(max) => G.point(MaxLength[B](max))
case Min(min) => G.point(Min[B](min))
case Max(max) => G.point(Max[B](max))
case After(start) => G.point(After[B](start))
case Before(end) => G.point(Before[B](end))
case AndThen(lhs, rhs) => G.apply2(f(lhs), f(rhs))(AndThen.apply)
}
}

implicit def formShow[L](implicit showC: Show[Constraint]) = new Delay[Show, FormF[?]] {
implicit def formShow: Delay[Show, FormF] = new Delay[Show, FormF] {
def apply[A](showA: Show[A]): Show[FormF[A]] = new Show[FormF[A]] {
override def show(f: FormF[A]): Cord = f match {
case Optional(a) =>
Expand All @@ -50,32 +64,25 @@ object Definition {
case Choice(alt) =>
Cord("[ ") ++ Cord.mkCord(Cord(" | "), alt.map { case (k, v) => Cord(s"$k:") ++ showA.show(v) }: _*) ++ Cord(
" ]")
case Sequence(elem) =>
showA.show(elem) ++ Cord("*")
case Value(c) =>
showC.show(c)
case Sequence(elems) =>
Cord.mkCord(Cord(", "), elems.map(showA.show): _*) ++ Cord("*")
case terminal => Cord(terminal.toString)
}
}
}

private def parse[O](unsafeParse: String => form.FormValue): form.Constraint =
Kleisli[\/[String, ?], String, FormValue]((s: String) =>
\/.fromTryCatchNonFatal(unsafeParse(s)).leftMap(e => s"For input string $s, got ${e.getClass}: ${e.getMessage}"))

def optional[T[_[_]], L](form: T[FormF])(implicit T: CorecursiveT[T]): T[FormF] =
T.embedT[FormF](Optional(form))
def mapping[T[_[_]], L](fields: (String, T[FormF])*)(implicit T: CorecursiveT[T]): T[FormF] =
T.embedT[FormF](Fields(fields.toVector))
def choice[T[_[_]], L](alternatives: (String, T[FormF])*)(implicit T: CorecursiveT[T]): T[FormF] =
T.embedT[FormF](Choice(alternatives.toVector))
def sequence[T[_[_]]](element: T[FormF])(implicit T: CorecursiveT[T]): T[FormF] =
T.embedT[FormF](Sequence(element))

def int: Constraint = parse(s => ValueNum(s.toInt))
T.embedT[FormF](Sequence(Vector(element)))

def isoDateTime = parse(s => ValueDate(ZonedDateTime.parse(s, DateTimeFormatter.ISO_DATE_TIME)))
def int[T[_[_]]](implicit T: CorecursiveT[T]) = T.embedT[FormF](Number())

implicit def lift[T[_[_]]](f: Constraint)(implicit T: CorecursiveT[T]): T[Definition.FormF] =
T.embedT[Definition.FormF](Definition.Value(f))
def isoDateTime[T[_[_]]](implicit T: CorecursiveT[T]) = T.embedT[FormF](IsoDateTime())

def andThen[T[_[_]]](lhs: T[FormF], rhs: T[FormF])(implicit T: CorecursiveT[T]) = T.embedT[FormF](AndThen(lhs, rhs))
}
8 changes: 6 additions & 2 deletions core/src/main/scala/pear/form/FormValue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import java.time.ZonedDateTime

sealed trait FormValue extends Product with Serializable
final case class ValueObject(fields: Map[String, FormValue]) extends FormValue
final case class ValueList(elements: List[FormValue]) extends FormValue
final case class ValueList(elements: Vector[FormValue]) extends FormValue
final case class ValueStr(value: String) extends FormValue
final case class ValueNum(value: Int) extends FormValue
final case class ValueNum(value: BigDecimal) extends FormValue
final case class ValueDate(value: ZonedDateTime) extends FormValue
final case class ValueBool(value: Boolean) extends FormValue
case object ValueNull extends FormValue

object FormValue {
def empty: FormValue = ValueNull
}

final case class Error(path: Path, message: String) {
override def toString: String = s"$path: $message"
}
52 changes: 0 additions & 52 deletions core/src/main/scala/pear/form/Validation.scala

This file was deleted.

Loading