Packages
ash
2.14.12
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/actions/sort.ex
defmodule Ash.Actions.Sort do
@moduledoc false
alias Ash.Error.Query.{
AggregatesNotSupported,
InvalidSortOrder,
NoSuchAttribute,
UnsortableAttribute
}
@sort_orders [:asc, :desc, :asc_nils_first, :asc_nils_last, :desc_nils_first, :desc_nils_last]
def process(_resource, empty, _aggregates, context \\ %{})
def process(_resource, empty, _aggregates, _context) when empty in [nil, []], do: {:ok, []}
def process(resource, sort, aggregates, context) when is_list(sort) do
sort
|> Enum.map(fn
{key, {order, context}} when is_atom(order) ->
{key, {order, context}}
{key, val} ->
if is_atom(val) do
{key, val}
else
{key, {:asc, val}}
end
val ->
{val, :asc}
end)
|> Enum.reduce({[], []}, fn
{field, {inner_order, _} = order}, {sorts, errors} when inner_order in @sort_orders ->
case Ash.Resource.Info.calculation(resource, field) do
nil ->
{sorts,
[
"Cannot provide context to a non-calculation field while sorting"
| errors
]}
calc ->
{module, opts} = calc.calculation
Code.ensure_compiled!(module)
if function_exported?(module, :expression, 2) do
if Ash.DataLayer.data_layer_can?(resource, :expression_calculation_sort) do
calculation_sort(
field,
calc,
module,
opts,
calc.type,
calc.constraints,
order,
sorts,
errors,
context
)
else
{sorts, ["Datalayer cannot sort on calculations"]}
end
else
{sorts, ["Calculations cannot be sorted on unless they define an expression"]}
end
end
{%Ash.Query.Calculation{} = calc, order}, {sorts, errors} ->
if order in @sort_orders do
{sorts ++ [{calc, order}], errors}
else
{sorts, [InvalidSortOrder.exception(order: order) | errors]}
end
{field, order}, {sorts, errors} when order in @sort_orders ->
attribute = Ash.Resource.Info.attribute(resource, field)
cond do
aggregate = Ash.Resource.Info.aggregate(resource, field) ->
aggregate_sort(aggregates, aggregate, order, resource, sorts, errors)
Map.has_key?(aggregates, field) ->
aggregate_sort(aggregates, field, order, resource, sorts, errors)
calc = Ash.Resource.Info.calculation(resource, field) ->
{module, opts} = calc.calculation
Code.ensure_compiled(module)
if :erlang.function_exported(module, :expression, 2) do
if Ash.DataLayer.data_layer_can?(resource, :expression_calculation_sort) do
calculation_sort(
field,
calc,
module,
opts,
calc.type,
calc.constraints,
order,
sorts,
errors,
context
)
else
{sorts, ["Datalayer cannot sort on calculations"]}
end
else
{sorts, ["Calculations cannot be sorted on unless they define an expression"]}
end
!attribute ->
{sorts, [NoSuchAttribute.exception(name: field, resource: resource) | errors]}
Ash.Type.embedded_type?(attribute.type) ->
{sorts, ["Cannot sort on embedded types" | errors]}
match?({:array, _}, attribute.type) ->
{sorts, ["Cannot sort on array types" | errors]}
!Ash.DataLayer.data_layer_can?(
resource,
{:sort, Ash.Type.storage_type(attribute.type, attribute.constraints)}
) ->
{sorts,
[
UnsortableAttribute.exception(field: field)
| errors
]}
true ->
{sorts ++ [{field, order}], errors}
end
{_, order}, {sorts, errors} ->
{sorts, [InvalidSortOrder.exception(order: order) | errors]}
end)
|> case do
{sorts, []} -> {:ok, sorts}
{_, errors} -> {:error, errors}
end
end
def sorting_on_identity?(%{sort: nil}), do: false
def sorting_on_identity?(query) do
identity_keys =
query.resource
|> Ash.Resource.Info.identities()
|> Enum.map(& &1.keys)
count_of_sort = Enum.count(query.sort)
Enum.any?([Ash.Resource.Info.primary_key(query.resource) | identity_keys], fn keyset ->
last_n_fields = query.sort |> Enum.reverse() |> Enum.take(count_of_sort)
Enum.all?(keyset, fn key ->
Enum.any?(last_n_fields, fn
{sort, _} when is_atom(sort) ->
sort == key
_ ->
false
end)
end)
end)
end
defp aggregate_sort(aggregates, field, order, resource, sorts, errors) do
{field, type} =
case field do
field when is_atom(field) ->
aggregate = Map.get(aggregates, field)
{field, {:ok, aggregate.type}}
%{name: name, kind: :custom, type: type} ->
{name, type}
%Ash.Resource.Aggregate{} = agg ->
attribute_type =
if agg.field do
related = Ash.Resource.Info.related(resource, agg.relationship_path)
Ash.Resource.Info.attribute(related, agg.field).type
end
{agg.name, Ash.Query.Aggregate.kind_to_type(agg.kind, attribute_type)}
end
case type do
{:ok, type} ->
if Ash.DataLayer.data_layer_can?(resource, :aggregate_sort) &&
Ash.DataLayer.data_layer_can?(
resource,
# do we need to get actual constraints for aggregates here?
{:sort, Ash.Type.storage_type(type, [])}
) do
{sorts ++ [{field, order}], errors}
else
{sorts,
[AggregatesNotSupported.exception(resource: resource, feature: "sorting") | errors]}
end
{:error, error} ->
{sorts, [error | errors]}
end
end
defp calculation_sort(
field,
calc,
module,
opts,
type,
constraints,
order,
sorts,
errors,
context
) do
{order, calc_context} =
case order do
order when is_atom(order) ->
{order, %{}}
{order, value} when is_list(value) ->
{order, Map.new(value)}
{order, value} when is_map(value) ->
{order, value}
other ->
{other, %{}}
end
with {:ok, input} <- Ash.Query.validate_calculation_arguments(calc, calc_context),
{:ok, calc} <-
Ash.Query.Calculation.new(
field,
module,
opts,
{type, constraints},
Map.put(input, :context, context),
calc.filterable?,
calc.load
) do
calc = Map.put(calc, :load, field)
{sorts ++ [{calc, order}], errors}
else
{:error, error} ->
{sorts, [error | errors]}
end
end
@doc """
Sort records at runtime
Opts
* `:api` - The api to use if data needs to be loaded
* `:lazy?` - Wether to use already loaded values or to re-load them when necessary. Defaults to `false`
"""
def runtime_sort(results, sort, opts \\ [])
def runtime_sort([], _empty, _), do: []
def runtime_sort(results, empty, _) when empty in [nil, []], do: results
def runtime_sort([single_result], _, _), do: [single_result]
def runtime_sort([%resource{} | _] = results, [{field, direction} | rest], opts) do
results
|> load_field(field, resource, opts)
|> Enum.group_by(&resolve_field(&1, field, resource, api: opts))
|> sort_by(fn {key, _value} -> key end, direction)
|> Enum.flat_map(fn {_, records} ->
runtime_sort(records, rest, Keyword.put(opts, :rekey?, false))
end)
|> maybe_rekey(results, resource, Keyword.get(opts, :rekey?, true))
end
defp maybe_rekey(new_results, results, resource, true) do
Enum.map(new_results, fn new_result ->
Enum.find(results, fn result ->
resource.primary_key_matches?(new_result, result)
end)
end)
end
defp maybe_rekey(new_results, _, _, _), do: new_results
def runtime_distinct(results, sort, opts \\ [])
def runtime_distinct([], _empty, _), do: []
def runtime_distinct(results, empty, _) when empty in [nil, []], do: results
def runtime_distinct([single_result], _, _), do: [single_result]
def runtime_distinct([%resource{} | _] = results, [{field, direction} | rest], opts) do
results
|> load_field(field, resource, opts)
|> Enum.group_by(&resolve_field(&1, field, resource, api: opts))
|> sort_by(fn {key, _value} -> key end, direction)
|> Enum.map(fn {_key, [first | _]} ->
first
end)
|> runtime_distinct(rest, Keyword.put(opts, :rekey?, false))
|> maybe_rekey(results, resource, Keyword.get(opts, :rekey?, true))
end
defp load_field(records, field, resource, opts) do
if is_nil(opts[:api]) || (opts[:lazy?] && Ash.Resource.loaded?(records, field)) do
records
else
query =
resource
|> Ash.Query.load(field)
|> Ash.Query.set_context(%{private: %{internal?: true}})
opts[:api].load!(records, query)
end
end
defp resolve_field(record, %Ash.Query.Calculation{} = calc, resource, opts) do
cond do
:erlang.function_exported(calc.module, :calculate, 3) ->
context = Map.put(calc.context, :api, opts[:api])
calc.module.calculate([record], calc.opts, context)
:erlang.function_exported(calc.module, :expression, 2) ->
expression = calc.module.expression(calc.opts, calc.context)
case Ash.Filter.hydrate_refs(expression, %{
resource: resource,
aggregates: %{},
calculations: %{},
public?: false
}) do
{:ok, expression} ->
case Ash.Expr.eval_hydrated(expression, record: record, resource: resource) do
{:ok, value} ->
{:ok, value}
_ ->
nil
end
_ ->
nil
end
true ->
nil
end
end
defp resolve_field(record, field, _resource, _) do
Map.get(record, field)
end
# :asc/:desc added to elixir in 1.10. sort_by and to_sort_by_fun copied from core
defp sort_by(enumerable, mapper, sorter) do
enumerable
|> Enum.map(&{&1, mapper.(&1)})
|> Enum.sort(to_sort_by_fun(sorter))
|> Enum.map(&elem(&1, 0))
end
defp to_sort_by_fun(sorter) when is_function(sorter, 2),
do: &sorter.(elem(&1, 1), elem(&2, 1))
defp to_sort_by_fun(:desc) do
to_sort_by_fun(:desc_nils_first)
end
defp to_sort_by_fun(:asc) do
to_sort_by_fun(:asc_nils_last)
end
defp to_sort_by_fun(:asc_nils_last) do
fn x, y ->
if is_nil(elem(x, 1)) && !is_nil(elem(y, 1)) do
false
else
Comp.less_or_equal?(elem(x, 1), elem(y, 1))
end
end
end
defp to_sort_by_fun(:asc_nils_first) do
fn x, y ->
if is_nil(elem(x, 1)) && !is_nil(elem(y, 1)) do
true
else
Comp.less_or_equal?(elem(x, 1), elem(y, 1))
end
end
end
defp to_sort_by_fun(:desc_nils_first) do
fn x, y ->
if is_nil(elem(x, 1)) && !is_nil(elem(y, 1)) do
true
else
Comp.greater_or_equal?(elem(x, 1), elem(y, 1))
end
end
end
defp to_sort_by_fun(:desc_nils_last) do
fn x, y ->
if is_nil(elem(x, 1)) && !is_nil(elem(y, 1)) do
false
else
Comp.greater_or_equal?(elem(x, 1), elem(y, 1))
end
end
end
defp to_sort_by_fun({direction, _input}) do
to_sort_by_fun(direction)
end
defp to_sort_by_fun(module) when is_atom(module),
do: &(module.compare(elem(&1, 1), elem(&2, 1)) != :gt)
defp to_sort_by_fun({:asc, module}) when is_atom(module),
do: &(module.compare(elem(&1, 1), elem(&2, 1)) != :gt)
defp to_sort_by_fun({:desc, module}) when is_atom(module),
do: &(module.compare(elem(&1, 1), elem(&2, 1)) != :lt)
end