defmodule Talos.Types.StringType do @moduledoc """ Type for check value is string For example: ```elixir iex> short_domain = %Talos.Types.StringType{length: 3} iex> domains_list = ["cats", "foo", "baz", "pron"] iex> Enum.filter(domains_list, fn str -> Talos.valid?(short_domain, str) end) ["foo", "baz"] ``` Additional parameters: `min_length`, same as `String.length(str) <= max_length` `max_length`, same as `String.length(str) >= max_length` `length`, same as `String.length(str) >= length` `regexp`, same as `String.match?(str, regexp)` """ defstruct [:min_length, :length, :max_length, :regexp, allow_nil: false, allow_blank: false] @type t :: %{ __struct__: atom, min_length: nil | integer, length: nil | integer, max_length: nil | integer, allow_nil: nil | boolean, allow_blank: nil | boolean, regexp: nil | Regex.t() } @behaviour Talos.Types @spec valid?(Talos.Types.StringType.t(), any) :: boolean def valid?(type, value) do errors(type, value) == [] end @spec errors(Talos.Types.StringType.t(), binary) :: list(String.t()) def errors(%__MODULE__{allow_blank: true}, "") do [] end def errors(%__MODULE__{allow_nil: true}, nil) do [] end def errors( %__MODULE__{regexp: regexp, min_length: min_len, length: len, max_length: max_len}, value ) do errors = case String.valid?(value) do true -> str_len = String.length(value) [ {is_nil(min_len) || min_len <= str_len, "minimum length: #{min_len}"}, {is_nil(len) || len == str_len, "length should be equal #{len}"}, {is_nil(max_len) || str_len <= max_len, "maximum length #{max_len}"}, {is_nil(regexp) || Regex.match?(regexp, value), "should match given regexp #{inspect(regexp)}"} ] |> Enum.filter(fn {bool, _} -> bool == false end) |> Enum.map(fn {_, error_text} -> error_text end) false -> ["should be StringType"] end case errors == [] do true -> [] false -> [inspect(value)] ++ errors end end end