|
1 | 1 | defmodule OpenApiSpex.Cast.String do
|
2 |
| - @moduledoc false |
3 |
| - alias OpenApiSpex.Cast |
| 2 | + @moduledoc """ |
4 | 3 |
|
5 |
| - def cast(%{value: value} = ctx) when is_binary(value) do |
6 |
| - case cast_binary(ctx) do |
7 |
| - {:cast, ctx} -> cast(ctx) |
8 |
| - result -> result |
9 |
| - end |
10 |
| - end |
| 4 | + This module will cast a binary to either an Elixir DateTime or Date. Otherwise it will |
| 5 | + validate a binary based on maxLength, minLength, or a Regex pattern passed through the |
| 6 | + schema struct. |
11 | 7 |
|
12 |
| - def cast(ctx) do |
13 |
| - Cast.error(ctx, {:invalid_type, :string}) |
14 |
| - end |
| 8 | + """ |
| 9 | + alias OpenApiSpex.{Cast, Cast.Error} |
15 | 10 |
|
16 |
| - ## Private functions |
| 11 | + @schema_fields [:maxLength, :minLength, :pattern] |
17 | 12 |
|
18 |
| - defp cast_binary(%{value: value, schema: %{format: :"date-time"}} = ctx) |
19 |
| - when is_binary(value) do |
20 |
| - case DateTime.from_iso8601(value) do |
21 |
| - {:ok, %DateTime{}, _offset} -> Cast.success(ctx, :format) |
22 |
| - _ -> Cast.error(ctx, {:invalid_format, :"date-time"}) |
| 13 | + def cast(%{value: value, schema: %{format: :date}} = ctx) when is_binary(value) do |
| 14 | + case Date.from_iso8601(value) do |
| 15 | + {:ok, %Date{} = date} -> |
| 16 | + {:ok, date} |
| 17 | + |
| 18 | + _ -> |
| 19 | + Cast.error(ctx, {:invalid_format, :date}) |
23 | 20 | end
|
24 | 21 | end
|
25 | 22 |
|
26 |
| - defp cast_binary(%{value: value, schema: %{format: :date}} = ctx) do |
27 |
| - case Date.from_iso8601(value) do |
28 |
| - {:ok, %Date{}} -> Cast.success(ctx, :format) |
29 |
| - _ -> Cast.error(ctx, {:invalid_format, :date}) |
| 23 | + def cast(%{value: value, schema: %{format: :"date-time"}} = ctx) when is_binary(value) do |
| 24 | + case DateTime.from_iso8601(value) do |
| 25 | + {:ok, %DateTime{} = datetime, _offset} -> |
| 26 | + {:ok, datetime} |
| 27 | + |
| 28 | + _ -> |
| 29 | + Cast.error(ctx, {:invalid_format, :"date-time"}) |
30 | 30 | end
|
31 | 31 | end
|
32 | 32 |
|
33 |
| - defp cast_binary(%{value: value, schema: %{pattern: pattern}} = ctx) when not is_nil(pattern) do |
34 |
| - if Regex.match?(pattern, value) do |
35 |
| - Cast.success(ctx, :pattern) |
| 33 | + def cast(%{value: value} = ctx) when is_binary(value) do |
| 34 | + apply_validation(ctx, @schema_fields) |
| 35 | + end |
| 36 | + |
| 37 | + def cast(ctx) do |
| 38 | + Cast.error(ctx, {:invalid_type, :string}) |
| 39 | + end |
| 40 | + |
| 41 | + ## Private functions |
| 42 | + |
| 43 | + defp apply_validation(%{value: value, schema: %{maxLength: max_length}} = ctx, [ |
| 44 | + :maxLength | fields |
| 45 | + ]) |
| 46 | + when is_integer(max_length) do |
| 47 | + if String.length(value) > max_length do |
| 48 | + ctx |
| 49 | + |> apply_error({:max_length, max_length}) |
| 50 | + |> apply_validation(fields) |
36 | 51 | else
|
37 |
| - Cast.error(ctx, {:invalid_format, pattern}) |
| 52 | + apply_validation(ctx, fields) |
38 | 53 | end
|
39 | 54 | end
|
40 | 55 |
|
41 |
| - defp cast_binary(%{value: value, schema: %{minLength: min_length}} = ctx) |
42 |
| - when is_integer(min_length) do |
| 56 | + defp apply_validation(%{value: value, schema: %{minLength: min_length}} = ctx, [ |
| 57 | + :minLength | fields |
| 58 | + ]) |
| 59 | + when is_integer(min_length) do |
43 | 60 | if String.length(value) < min_length do
|
44 |
| - Cast.error(ctx, {:min_length, min_length}) |
| 61 | + ctx |
| 62 | + |> apply_error({:min_length, min_length}) |
| 63 | + |> apply_validation(fields) |
45 | 64 | else
|
46 |
| - Cast.success(ctx, :minLength) |
| 65 | + apply_validation(ctx, fields) |
47 | 66 | end
|
48 | 67 | end
|
49 | 68 |
|
50 |
| - defp cast_binary(%{value: value, schema: %{maxLength: max_length}} = ctx) |
51 |
| - when is_integer(max_length) do |
52 |
| - if String.length(value) > max_length do |
53 |
| - Cast.error(ctx, {:max_length, max_length}) |
| 69 | + defp apply_validation(%{value: value, schema: %{pattern: pattern}} = ctx, [:pattern | fields]) |
| 70 | + when not is_nil(pattern) do |
| 71 | + if Regex.match?(pattern, value) do |
| 72 | + apply_validation(ctx, fields) |
54 | 73 | else
|
55 |
| - Cast.success(ctx, :maxLength) |
| 74 | + ctx |
| 75 | + |> apply_error({:invalid_format, pattern}) |
| 76 | + |> apply_validation(fields) |
56 | 77 | end
|
57 | 78 | end
|
58 | 79 |
|
59 |
| - defp cast_binary(ctx), do: Cast.ok(ctx) |
| 80 | + defp apply_validation(ctx, [_field | fields]), do: apply_validation(ctx, fields) |
| 81 | + defp apply_validation(%{value: value, errors: []}, []), do: {:ok, value} |
| 82 | + defp apply_validation(%{errors: errors}, []) when length(errors) > 0, do: {:error, errors} |
| 83 | + |
| 84 | + defp apply_error(%{errors: errors} = ctx, error_args) do |
| 85 | + Map.put(ctx, :errors, [Error.new(ctx, error_args) | errors]) |
| 86 | + end |
60 | 87 | end
|
0 commit comments