Skip to content

Commit

Permalink
feat(validation): add case class from string (#244)
Browse files Browse the repository at this point in the history
With LatLon case class, we can now validate and parse strings into
case classes.
  • Loading branch information
cheleb authored Nov 28, 2024
1 parent fca416c commit 3b88501
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
37 changes: 35 additions & 2 deletions examples/client/src/main/scala/samples/Validation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,54 @@ import io.github.iltotore.iron.constraint.all.*

val validation = {

case class LatLon(lat: Double, lon: Double) {
override def toString: String = s"$lat,$lon"
}

given Form[CurrencyCode] = stringForm(CurrencyCode(_))

given Form[LatLon] = stringFormWithValidation(using
new Validator[LatLon] {
override def validate(value: String): Either[String, LatLon] = {
value.split(",") match {
case Array(lat, lon) =>
(
lat.toDoubleOption.toRight("Invalid latitude"),
lon.toDoubleOption.toRight("Invalid longitude")
) match {
case (Right(lat), Right(lon)) => Right(LatLon(lat, lon))
case (Left(latError), Left(rightError)) =>
Left(s"$latError and $rightError")
case (Left(latError), _) => Left(latError)
case (_, Left(lonError)) => Left(lonError)
}
case _ => Left("Invalid format")
}
}
}
)

case class IronSample(
curenncyCode: CurrencyCode,
optional: Option[String],
optionalInt: Option[Int],
doubleGreaterThanEight: Double :| GreaterEqual[8.0],
optionalDoublePositive: Option[Double :| Positive]
optionalDoublePositive: Option[Double :| Positive],
latLong: LatLon
)

given Defaultable[Double :| GreaterEqual[8.0]] with
def default: Double :| GreaterEqual[8.0] = 8.0

val ironSampleVar = Var(
IronSample(CurrencyCode("Eur"), Some("name"), Some(1), 9.1, Some(1))
IronSample(
CurrencyCode("Eur"),
Some("name"),
Some(1),
9.1,
Some(1),
LatLon(1, 2)
)
)

val errorBus = ironSampleVar.errorBus
Expand Down
33 changes: 33 additions & 0 deletions modules/core/src/main/scala/dev/cheleb/scalamigen/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import com.raquo.laminar.api.L.*
import com.raquo.laminar.nodes.ReactiveHtmlElement
import org.scalajs.dom.HTMLElement

/** A form for a type A, no validation. Convenient to use for Opaque types. If
* you need validation, use a Form with a ValidationEvent.
*/
def stringForm[A](to: String => A) = new Form[A]:
override def render(
path: List[Symbol],
Expand Down Expand Up @@ -97,6 +100,36 @@ def enumForm[A](values: Array[A], f: Int => A) = new Form[A] {

}

/** A form for a type A, no validation. Convenient to use for Opaque types. If
* you need validation, use a Form with a ValidationEvent.
*/
def stringFormWithValidation[A](using
validator: Validator[A]
) = new Form[A]:
override def render(
path: List[Symbol],
variable: Var[A],
syncParent: () => Unit
)(using
factory: WidgetFactory,
errorBus: EventBus[(String, ValidationEvent)]
): HtmlElement =
factory.renderText.amend(
value <-- variable.signal.map(_.toString),
onInput.mapToValue.map(validator.validate) --> {
case Right(v) =>
variable.set(v)
errorBus.emit(
(path.key, ValidEvent)
)
syncParent()
case Left(err) =>
errorBus.emit(
(path.key, InvalideEvent(err))
)
}
)

/** Extension methods for the Var class.
*/
extension [A](va: Var[A])
Expand Down

0 comments on commit 3b88501

Please sign in to comment.