import gleam/list import gleam/result /// Key/Value pair for encoding validation errors. /// Key indicates the name of the field that failed validation /// and Value is the error message. pub type ValidationError { ValidationError(key: String, value: String) } /// Takes a validation result and list of validation errors /// If result is error, it adds it to the list pub fn append_error( result: Result(Nil, ValidationError), list: List(ValidationError), ) -> List(ValidationError) { case result { Error(validation_error) -> { list.append(list, [validation_error]) } Ok(Nil) -> list } } /// Takes a list of validation results and returns a list of the errors /// Returns an empty list if no errors pub fn collect_errors( inputs: List(Result(Nil, ValidationError)), ) -> List(ValidationError) { // filter for errors list.filter_map(inputs, fn(x) { case x { Ok(Nil) -> Error(Nil) Error(validation_error) -> Ok(validation_error) } }) } /// Takes a list of validation errors and converts it to a result /// Returns ok if list is empty pub fn prepare( errors: List(ValidationError), ) -> Result(Nil, List(ValidationError)) { case list.is_empty(errors) { True -> Ok(Nil) False -> Error(errors) } } /// Takes a list of validation errors and converts it to a result /// wrapping the errors in a custom type. Returns Ok(Nil) if list is empty. /// /// This functions similar to `valguard.prepare`. pub fn prepare_with( errors: List(ValidationError), custom_type, ) -> Result(Nil, custom_type) { case list.is_empty(errors) { True -> Ok(Nil) False -> Error(custom_type(errors)) } } /// Wrap list of errors from a Validation Result in a custom type. pub fn wrap_result( result: Result(Nil, List(ValidationError)), custom_type, ) -> Result(Nil, custom_type) { result.map_error(result, fn(errors) { custom_type(errors) }) } /// Validates a single validation function @deprecated("Use function `with` instead") pub fn single( key: String, validation_result: Result(Nil, String), ) -> Result(Nil, ValidationError) { case validation_result { Ok(Nil) -> Ok(Nil) Error(value) -> Error(ValidationError(key:, value:)) } } /// Validates a list of requirements lazily. /// Returns Ok(Nil) if success, Error(String) at first issue pub fn list( key: String, list: List(fn() -> Result(Nil, String)), ) -> Result(Nil, ValidationError) { // recursively check list of validations case list_inner(list, Ok(Nil)) { Ok(Nil) -> Ok(Nil) Error(value) -> Error(ValidationError(key:, value:)) } } /// Inner recursive loop for lazy_validate fn list_inner( list: List(fn() -> Result(Nil, String)), prev: Result(Nil, String), ) -> Result(Nil, String) { // return early if previous value was error use _ <- result.try(prev) case list { [] -> prev [next, ..rest] -> list_inner(rest, next()) } } /// Takes a key, value and list of validation functions. /// Runs then lazily and returns the result. pub fn with( key: String, value, list: List(fn(value) -> Result(Nil, String)), ) -> Result(Nil, ValidationError) { // recursively check list of validations case with_inner(list, value, Ok(Nil)) { Ok(Nil) -> Ok(Nil) Error(value) -> Error(ValidationError(key:, value:)) } } /// Inner recursive loop for validate_with fn with_inner( list: List(fn(value) -> Result(Nil, String)), value, prev: Result(Nil, String), ) -> Result(Nil, String) { // return early if previous value was error use _ <- result.try(prev) case list { [] -> prev [next, ..rest] -> with_inner(rest, value, next(value)) } }