Packages

A delightful ORM (?) for Gleam! 🍳

Current section

Files

Jump to
ormlette src ormlette_validate valid.gleam
Raw

src/ormlette_validate/valid.gleam

import gleam/dict
import gleam/dynamic
import gleam/int
import gleam/io
import gleam/list
import gleam/regex
import gleam/string
pub type Validator(input) {
Validator(fn(input) -> Result(input, List(String)))
}
pub type FieldValidator {
FieldValidator(String, Validator(dynamic.Dynamic))
}
pub fn validate(
input: input,
validators: List(Validator(input)),
) -> Result(input, List(String)) {
list.fold(validators, Ok(input), fn(acc, v) {
case v {
Validator(f) -> {
case acc {
Ok(val) -> f(val)
Error(errs) ->
case f(input) {
Ok(_) -> acc
Error(new_errs) -> Error(list.append(errs, new_errs))
}
}
}
}
})
}
pub fn validate_dict(
input: dict.Dict(String, dynamic.Dynamic),
field_validators: List(FieldValidator),
) -> Result(dict.Dict(String, dynamic.Dynamic), List(String)) {
list.fold(field_validators, Ok(input), fn(acc, field_validator) {
case field_validator {
FieldValidator(field, Validator(f)) -> {
case dict.get(input, field) {
Ok(value) -> {
case f(value) {
Ok(_) -> acc
Error(new_errs) -> {
case acc {
Ok(_) -> Error(new_errs)
Error(errs) -> Error(list.append(errs, new_errs))
}
}
}
}
Error(_) -> acc
}
}
}
})
}
pub fn is_not_empty() -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.string(input) {
Ok(value) -> {
case value == "" {
True -> Error(["Value cannot be empty"])
False -> Ok(input)
}
}
Error(_) -> Error(["Value is not a string"])
}
})
}
pub fn has_min_length(min_length: Int) -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.string(input) {
Ok(value) -> {
case string.length(value) < min_length {
True ->
Error([
"Value must have at least "
<> int.to_string(min_length)
<> " characters",
])
False -> Ok(input)
}
}
Error(_) -> Error(["Value is not a string"])
}
})
}
pub fn is_number() -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.string(input) {
Ok(value) -> {
case int.parse(value) {
Ok(_) -> Ok(input)
Error(_) -> Error(["Value must be a number"])
}
}
Error(_) -> {
case dynamic.int(input) {
Ok(_) -> Ok(input)
Error(_) -> Error(["Value is not a number!"])
}
}
}
})
}
pub fn min(num: Int) -> Validator(dynamic.Dynamic) {
Validator(fn(input: dynamic.Dynamic) {
let value_ok = dynamic.int(input)
case value_ok {
Ok(value) -> {
case value >= num {
True -> Ok(input)
False ->
Error([
"Value must be greater than or equal to " <> int.to_string(num),
])
}
}
Error(_) -> Error(["Not an int!"])
}
})
}
pub fn max(num: Int) -> Validator(dynamic.Dynamic) {
Validator(fn(input: dynamic.Dynamic) {
let value_ok = dynamic.int(input)
case value_ok {
Ok(value) -> {
case value <= num {
True -> Ok(input)
False ->
Error(["Value must be less than or equal to " <> int.to_string(num)])
}
}
Error(_) -> Error(["Not an int!"])
}
})
}
pub fn is_boolean() -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.bool(input) {
Ok(_) -> Ok(input)
Error(_) -> Error(["Value must be a boolean (true or false)"])
}
})
}
pub fn matches_regex(pattern: String) -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.string(input) {
Ok(value) -> {
let assert Ok(regex_pattern) = regex.from_string(pattern)
case regex.check(regex_pattern, value) {
True -> Ok(input)
False ->
Error(["Value does not match the required pattern: " <> pattern])
}
}
Error(_) -> Error(["Value is not a string"])
}
})
}
pub fn is_in_list(
allowed_values: List(dynamic.Dynamic),
) -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case list.contains(allowed_values, input) {
True -> Ok(input)
False ->
Error([
"Value must be one of: "
<> string.join(
list.map(allowed_values, fn(a) {
case dynamic.string(a) {
Ok(v) -> v
Error(_) -> ""
}
}),
",",
),
])
}
})
}
pub fn is_positive() -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.int(input) {
Ok(value) -> {
case value > 0 {
True -> Ok(input)
False -> Error(["Value must be positive"])
}
}
Error(_) -> Error(["Value is not a number"])
}
})
}
pub fn is_negative() -> Validator(dynamic.Dynamic) {
Validator(fn(input) {
case dynamic.int(input) {
Ok(value) -> {
case value < 0 {
True -> Ok(input)
False -> Error(["Value must be positive"])
}
}
Error(_) -> Error(["Value is not a number"])
}
})
}
// // Example usage
// pub fn main() {
// let input =
// dict.from_list([#("name", dynamic.from("John")), #("age", dynamic.from(30))])
// let validators = [
// FieldValidator("name", is_not_empty()),
// FieldValidator("age", is_number()),
// FieldValidator("age", min(10)),
// FieldValidator("age", max(20)),
// ]
// validate_dict(input, validators)
// |> io.debug
// }