-
Notifications
You must be signed in to change notification settings - Fork 3
/
sort_parser.ex
73 lines (57 loc) · 1.97 KB
/
sort_parser.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
defmodule Surgex.Parser.SortParser do
@moduledoc """
Parses the JSON API's sort parameter according to the
[JSON API spec](http://jsonapi.org/format/#fetching-sorting).
Produces a `{direction, column}` tuple, in which `direction` is either `:asc` or `:desc` and
`column` is a safely atomized and underscored column name.
"""
@doc false
@spec call(term(), [atom]) :: {:ok, {:asc | :desc, atom} | nil} | {:error, :invalid_sort_column}
def call(nil, _allowed_columns), do: {:ok, nil}
def call("", _allowed_columns), do: {:ok, nil}
def call(input, allowed_columns) when is_binary(input) do
case input do
"-" <> column ->
validate_allowed_columns(column, allowed_columns, :desc)
column ->
validate_allowed_columns(column, allowed_columns, :asc)
end
end
def call(_input, _), do: {:error, :invalid_sort_column}
defp validate_allowed_columns(column, allowed_columns, direction) do
column_atom = atomize_maybe_dasherized(column)
if column_atom && column_atom in allowed_columns do
{:ok, {direction, column_atom}}
else
{:error, :invalid_sort_column}
end
end
defp atomize_maybe_dasherized(string) do
atomize(string) ||
string
|> String.replace("-", "_")
|> atomize
end
defp atomize(string) do
String.to_existing_atom(string)
rescue
ArgumentError -> nil
end
@doc """
Flattens the result of the parser (sort tuple) into `*_by` and `*_direction` keys.
## Examples
iex> SortParser.flatten({:ok, sort: {:asc, :col}}, :sort)
{:ok, sort_by: :col, sort_direction: :asc}
"""
@spec flatten({:ok, Keyword.t()}, atom) :: {:ok, {:asc | :desc, atom}}
def flatten({:ok, opts}, key) do
case Keyword.pop(opts, key) do
{nil, _} ->
{:ok, opts}
{{direction, column}, rem_opts} ->
final_opts = Keyword.merge(rem_opts, sort_by: column, sort_direction: direction)
{:ok, final_opts}
end
end
def flatten(input, _key), do: input
end