Packages
zoi
0.11.1
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.1
0.9.0
0.9.0-rc.1
0.9.0-rc.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Zoi is a schema validation library for Elixir, designed to provide a simple and flexible way to define and validate data.
Current section
Files
Jump to
Current section
Files
lib/zoi/types/string.ex
defmodule Zoi.Types.String do
@moduledoc false
use Zoi.Type.Def, fields: [:min_length, :max_length, :length, coerce: false]
alias Zoi.Validations
def opts() do
error = "invalid type: expected integer"
Zoi.Opts.meta_opts()
|> Zoi.Opts.with_coerce()
|> Zoi.Types.Extend.new(
min_length:
Zoi.Opts.constraint_schema(Zoi.Types.Integer.new([]),
description: "string minimum length",
error: error
),
max_length:
Zoi.Opts.constraint_schema(Zoi.Types.Integer.new([]),
description: "string maximum length",
error: error
),
length:
Zoi.Opts.constraint_schema(Zoi.Types.Integer.new([]),
description: "string exact length",
error: error
)
)
end
def new(opts \\ []) do
{validation_opts, opts} = Keyword.split(opts, [:min_length, :max_length, :length])
opts
|> apply_type()
|> Validations.maybe_set_validation(Validations.Gte, validation_opts[:min_length])
|> Validations.maybe_set_validation(Validations.Lte, validation_opts[:max_length])
|> Validations.maybe_set_validation(Validations.Length, validation_opts[:length])
end
defimpl Zoi.Type do
def parse(schema, input, opts) do
coerce = Keyword.get(opts, :coerce, schema.coerce)
with {:ok, parsed} <- parse_type(input, coerce, schema),
:ok <- validate_constraints(schema, parsed) do
{:ok, parsed}
end
end
defp parse_type(input, coerce, schema) do
cond do
is_binary(input) -> {:ok, input}
coerce -> {:ok, to_string(input)}
true -> error(schema)
end
end
defp validate_constraints(schema, input) do
[
{Validations.Length, schema.length},
{Validations.Gte, schema.min_length},
{Validations.Lte, schema.max_length}
]
|> Validations.run_validations(schema, input)
end
defp error(schema) do
{:error, Zoi.Error.invalid_type(:string, error: schema.meta.error)}
end
def type_spec(_schema, _opts) do
quote(do: binary())
end
end
defimpl Zoi.Validations.Gte do
def set(schema, value, opts \\ []) do
%{schema | min_length: {value, opts}, length: nil}
end
def validate(_schema, input, value, opts) do
if String.length(input) >= value do
:ok
else
{:error, Zoi.Error.greater_than_or_equal_to(:string, value, opts)}
end
end
end
defimpl Zoi.Validations.Lte do
def set(schema, value, opts \\ []) do
%{schema | max_length: {value, opts}, length: nil}
end
def validate(_schema, input, value, opts) do
if String.length(input) <= value do
:ok
else
{:error, Zoi.Error.less_than_or_equal_to(:string, value, opts)}
end
end
end
defimpl Zoi.Validations.Length do
def set(schema, value, opts \\ []) do
%{schema | length: {value, opts}, min_length: nil, max_length: nil}
end
def validate(_schema, input, value, opts) do
if String.length(input) == value do
:ok
else
{:error, Zoi.Error.invalid_length(:string, value, opts)}
end
end
end
defimpl Zoi.Validations.Url do
def validate(_schema, input, opts) do
uri = URI.parse(input)
if uri.scheme in ["http", "https"] and uri.host != nil do
:ok
else
{:error, Zoi.Error.invalid_url(input, opts)}
end
end
end
defimpl Zoi.Validations.StartsWith do
def validate(_schema, input, prefix, opts) do
if String.starts_with?(input, prefix) do
:ok
else
{:error, Zoi.Error.invalid_starting_string(prefix, opts)}
end
end
end
defimpl Zoi.Validations.EndsWith do
def validate(_schema, input, suffix, opts) do
if String.ends_with?(input, suffix) do
:ok
else
{:error, Zoi.Error.invalid_ending_string(suffix, opts)}
end
end
end
defimpl Zoi.Validations.Regex do
def validate(_schema, input, regex, regex_opts, opts) do
# To allow both string and regex input for regex refinement
regex = Regex.compile!(regex, regex_opts)
if String.match?(input, regex) do
:ok
else
{:error, Zoi.Error.invalid_format(regex, opts)}
end
end
end
defimpl Inspect do
alias Zoi.Validations
def inspect(type, opts) do
extra_fields = [
min_length: Validations.unwrap_validation(type.min_length),
max_length: Validations.unwrap_validation(type.max_length),
length: Validations.unwrap_validation(type.length)
]
Zoi.Inspect.build(type, opts, extra_fields)
end
end
defimpl Zoi.JSONSchema.Encoder do
def encode(schema) do
%{type: :string}
|> maybe_add(:minLength, schema.min_length)
|> maybe_add(:maxLength, schema.max_length)
|> maybe_add_length(schema.length)
end
defp maybe_add(map, _key, nil), do: map
defp maybe_add(map, key, {value, _opts}), do: Map.put(map, key, value)
defp maybe_add_length(map, nil), do: map
defp maybe_add_length(map, {value, _opts}),
do: map |> Map.put(:minLength, value) |> Map.put(:maxLength, value)
end
end