mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 21:43:02 +12:00
01abe80a04
previously, ci_string would downcase all input/output automatically, throwing away the "representation" of the original input. Now, that is an option provided as a constraint, for example: `attribute :email, :ci_string, constraints: [casing: :lower]` or `attribute :serial, :ci_string, constraints: [casing: :upper]` All comparison logic remains the same, so the only thing that is affected is the `to_string(value)` logic, which now returns based on the configured casing. By default its just the value, but with `lower`/`upper` it will downcase/upcase the value accordingly.
106 lines
2.5 KiB
Elixir
106 lines
2.5 KiB
Elixir
defmodule Ash.CiString do
|
|
@moduledoc """
|
|
Represents a case insensitive string
|
|
|
|
While some data layers are aware of case insensitive string types, in order for values
|
|
of this type to be used in other parts of Ash Framework, it has to be embedded in a module
|
|
this allows us to implement the `Comparable` protocol for it.
|
|
|
|
For the type implementation, see `Ash.Type.CiString`
|
|
"""
|
|
|
|
defstruct [:string, casted?: false, case: nil]
|
|
|
|
def sigil_i(value, mods) do
|
|
cond do
|
|
?l in mods ->
|
|
new(value, :lower)
|
|
|
|
?u in mods ->
|
|
new(value, :upper)
|
|
|
|
true ->
|
|
new(value)
|
|
end
|
|
end
|
|
|
|
defimpl Jason.Encoder do
|
|
def encode(ci_string, opts) do
|
|
ci_string
|
|
|> Ash.CiString.value()
|
|
|> Jason.Encode.string(opts)
|
|
end
|
|
end
|
|
|
|
defimpl String.Chars do
|
|
def to_string(ci_string) do
|
|
Ash.CiString.value(ci_string)
|
|
end
|
|
end
|
|
|
|
defimpl Inspect do
|
|
import Inspect.Algebra
|
|
|
|
def inspect(%Ash.CiString{string: string}, opts) do
|
|
concat(["#Ash.CiString<", to_doc(string, opts), ">"])
|
|
end
|
|
end
|
|
|
|
def new(value, casing \\ nil) do
|
|
case casing do
|
|
:upper ->
|
|
%Ash.CiString{casted?: true, string: value && String.upcase(value)}
|
|
|
|
:lower ->
|
|
%Ash.CiString{casted?: true, string: value && String.downcase(value)}
|
|
|
|
nil ->
|
|
%Ash.CiString{casted?: false, string: value}
|
|
end
|
|
end
|
|
|
|
def value(%Ash.CiString{string: value, casted?: false, case: :lower}) do
|
|
value && String.downcase(value)
|
|
end
|
|
|
|
def value(%Ash.CiString{string: value, casted?: false, case: :upper}) do
|
|
value && String.upcase(value)
|
|
end
|
|
|
|
def value(%Ash.CiString{string: value}) do
|
|
value
|
|
end
|
|
|
|
def compare(left, right) do
|
|
do_compare(to_comparable_string(left), to_comparable_string(right))
|
|
end
|
|
|
|
defp do_compare(left, right) when left < right, do: :lt
|
|
defp do_compare(left, right) when left == right, do: :eq
|
|
defp do_compare(_, _), do: :gt
|
|
|
|
@doc "Returns the downcased value, only downcasing if it hasn't already been down"
|
|
def to_comparable_string(value) when is_binary(value) do
|
|
String.downcase(value)
|
|
end
|
|
|
|
def to_comparable_string(%__MODULE__{case: :lower, casted?: true, string: value}) do
|
|
value
|
|
end
|
|
|
|
def to_comparable_string(%__MODULE__{string: value}) do
|
|
value && String.downcase(value)
|
|
end
|
|
|
|
def to_comparable_string(nil), do: nil
|
|
end
|
|
|
|
use Comp
|
|
|
|
defcomparable left :: Ash.CiString, right :: BitString do
|
|
Ash.CiString.compare(left, right)
|
|
end
|
|
|
|
defcomparable left :: Ash.CiString, right :: Ash.CiString do
|
|
Ash.CiString.compare(left, right)
|
|
end
|