Validation for parameter type definitions in the DSL.
Parameters can have simple types (:float, :integer, etc.) or unit types
like {:unit, :meter}.
Numeric parameters - :float, :integer and unit types - can also declare
min/max bounds. option_type/3 folds those bounds into the
Spark.Options type generated for the parameter so that they are enforced
wherever a value is validated, and describe/1 recovers them from a
generated type for display.
Summary
Functions
Recovers the declared parameter type and its bounds from a generated
Spark.Options type.
Builds the Spark.Options type for a parameter of type, bounded by min
and max.
Validates a parameter type specification.
Validates a value against a bounded numeric parameter's type and bounds.
Types
@type bound() :: number() | Localize.Unit.t() | nil
@type t() :: :float | :integer | :boolean | :string | :atom | {:unit, atom()}
Functions
@spec describe(Spark.Options.type() | nil) :: {t() | Spark.Options.type() | nil, bound(), bound()}
Recovers the declared parameter type and its bounds from a generated
Spark.Options type.
Types which don't carry bounds - including the hand-written schemas of
components which use BB.Parameter - are returned unchanged with nil
bounds.
Examples
iex> BB.Parameter.Type.describe(:float)
{:float, nil, nil}
iex> {:ok, type} = BB.Parameter.Type.option_type(:integer, 0, 127)
iex> BB.Parameter.Type.describe(type)
{:integer, 0, 127}
iex> {:ok, type} = BB.Parameter.Type.option_type({:unit, :meter}, nil, Localize.Unit.new!(1, "meter"))
iex> BB.Parameter.Type.describe(type)
{{:unit, :meter}, nil, Localize.Unit.new!(1, "meter")}
@spec option_type(t(), bound(), bound()) :: {:ok, Spark.Options.type()} | {:error, String.t()}
Builds the Spark.Options type for a parameter of type, bounded by min
and max.
Either bound may be nil. Numeric bounds are plain numbers, bounds on a unit
type are Localize.Unit values compatible with the parameter's unit.
Examples
An unbounded type is used as-is:
iex> BB.Parameter.Type.option_type(:float, nil, nil)
{:ok, :float}Bounds wrap the type in a custom validator:
iex> BB.Parameter.Type.option_type(:integer, 0, 127)
{:ok, {:custom, BB.Parameter.Type, :validate_bounds, [[type: :integer, min: 0, max: 127]]}}Only one of the two is needed:
iex> BB.Parameter.Type.option_type(:float, 0.0, nil)
{:ok, {:custom, BB.Parameter.Type, :validate_bounds, [[type: :float, min: 0.0, max: nil]]}}Bounds on a unit type become unit constraints:
iex> BB.Parameter.Type.option_type({:unit, :meter}, nil, Localize.Unit.new!(1, "meter"))
{:ok, {:custom, BB.Unit.Option, :validate, [[compatible: :meter, max: Localize.Unit.new!(1, "meter")]]}}Non-numeric types cannot be bounded:
iex> BB.Parameter.Type.option_type(:string, 0, nil)
{:error, "`min` and `max` are only supported for numeric parameter types (:float, :integer or {:unit, unit_type}), got: :string"}
Validates a parameter type specification.
Returns {:ok, type} for valid types or {:error, message} for invalid ones.
Valid Types
- Simple types:
:float,:integer,:boolean,:string,:atom - Unit types:
{:unit, unit_type}whereunit_typeis a valid CLDR unit
Examples
iex> BB.Parameter.Type.validate(:float)
{:ok, :float}
iex> BB.Parameter.Type.validate({:unit, :meter})
{:ok, {:unit, :meter}}
iex> BB.Parameter.Type.validate(:invalid)
{:error, "Expected one of [:float, :integer, :boolean, :string, :atom] or {:unit, unit_type}, got: :invalid"}
Validates a value against a bounded numeric parameter's type and bounds.
This is the validator used by the type option_type/3 builds for a bounded
:float or :integer parameter.
Examples
iex> BB.Parameter.Type.validate_bounds(0.5, type: :float, min: 0.0, max: 1.0)
{:ok, 0.5}
iex> BB.Parameter.Type.validate_bounds(2.0, type: :float, min: 0.0, max: 1.0)
{:error, "expected value to be at most 1.0, got: 2.0"}
iex> BB.Parameter.Type.validate_bounds(-1.0, type: :float, min: 0.0, max: nil)
{:error, "expected value to be at least 0.0, got: -1.0"}
iex> BB.Parameter.Type.validate_bounds("nope", type: :float, min: 0.0, max: 1.0)
{:error, "expected float, got: \"nope\""}