Funx.Validate.Behaviour behaviour (funx v0.8.0)
View SourceBehaviour for validation functions.
All validators follow a consistent arity-3 signature, matching other DSL behaviours in Funx.
Contract
@callback validate(value :: any(), opts :: keyword(), env :: map()) ::
Funx.Monad.Either.t(any(), Funx.Errors.ValidationError.t())
| :ok
| {:ok, any()}
| {:error, Funx.Errors.ValidationError.t()}Arguments
value- The value to validate (may be transformed by previous validators)opts- Keyword list of options (validator-specific configuration)env- Environment map (runtime context like database connections, session data)
Return Values
Canonical (preferred):
Either.right(value)- Validation passed, return original or transformed valueEither.left(ValidationError.t())- Validation failed with error
Legacy (supported via normalization):
:ok- Validation passed, return original value{:ok, value}- Validation passed with transformation{:error, ValidationError.t()}- Validation failed
Semantic Rules
- Arguments strictly ordered: value, opts, env
- Either is canonical (tagged tuples normalized by DSL)
- Value transformation allowed (sequential within focus)
- Never raise for validation failure (use Left/error tuple)
- Return ValidationError for errors (not raw strings)
- Concurrency-safe by contract
Message Option
All validators should support a :message option for custom error messages:
- String:
[message: "custom error"] - Function:
[message: fn value -> "got #{inspect(value)}" end]
When a function is provided, it receives the current value being validated.
Example
defmodule MyValidator do
@behaviour Funx.Validate.Behaviour
alias Funx.Monad.Either
alias Funx.Errors.ValidationError
@impl true
def validate(value, opts, _env) do
if valid?(value) do
Either.right(value)
else
message = get_message(opts, value, "default error message")
Either.left(ValidationError.new(message))
end
end
defp get_message(opts, value, default) do
case Keyword.get(opts, :message) do
nil -> default
msg when is_binary(msg) -> msg
msg_fn when is_function(msg_fn, 1) -> msg_fn.(value)
end
end
end
Summary
Callbacks
@callback validate(value :: any(), opts :: keyword(), env :: map()) :: Funx.Monad.Either.t(any(), Funx.Errors.ValidationError.t()) | :ok | {:ok, any()} | {:error, Funx.Errors.ValidationError.t()}