Packages
localize
0.23.0
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.41.3
0.41.2
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.1
0.30.0
retired
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.1.0-alpha.1
Localization (parsing, formatting) of numbers, dates/time/calendar, units of measure, messages and lists. Includes localized collation.
Current section
Files
Jump to
Current section
Files
lib/localize/collation/nif.ex
defmodule Localize.Collation.Nif do
@moduledoc """
Collation NIF interface providing high-performance Unicode collation via ICU4C.
This module wraps the `nif_collation_cmp/10` function from `Localize.Nif` and
provides encoding of collation options to ICU enum values.
"""
alias Localize.Collation.Options
# Sentinel value meaning "use ICU default / no change"
@opt_default -1
# ICU UColAttributeValue enum values
@ucol_primary 0
@ucol_secondary 1
@ucol_quaternary 3
@ucol_identical 15
@ucol_on 17
@ucol_shifted 20
@ucol_lower_first 24
@ucol_upper_first 25
# ICU UScriptCode values (from uscript.h) and UColReorderCode values (from ucol.h)
@script_codes %{
:space => 0x1000,
:punct => 0x1001,
:punctuation => 0x1001,
:symbol => 0x1002,
:currency => 0x1003,
:digit => 0x1004,
:others => 0,
:Zzzz => 0,
:Arab => 2,
:Armn => 3,
:Beng => 4,
:Cyrl => 8,
:Deva => 10,
:Ethi => 11,
:Geor => 12,
:Grek => 14,
:Gujr => 15,
:Guru => 16,
:Hani => 17,
:Hang => 18,
:Hebr => 19,
:Hira => 20,
:Knda => 21,
:Kana => 22,
:Khmr => 23,
:Laoo => 24,
:Latn => 25,
:Mlym => 26,
:Mong => 27,
:Mymr => 28,
:Orya => 31,
:Sinh => 33,
:Taml => 35,
:Telu => 36,
:Thai => 38,
:Tibt => 39
}
@doc """
Returns whether the collation NIF backend is available.
### Returns
* `true` if the NIF shared library was loaded and the collation function is available.
* `false` otherwise.
"""
@spec available?() :: boolean()
def available? do
Localize.Nif.collation_available?()
end
@doc """
Compare two strings using the ICU NIF collator with full option support.
### Arguments
* `string_a` - the first string to compare.
* `string_b` - the second string to compare.
* `options` - a `%Localize.Collation.Options{}` struct.
### Returns
* `:lt` if `string_a` sorts before `string_b`.
* `:eq` if `string_a` and `string_b` are collation-equal.
* `:gt` if `string_a` sorts after `string_b`.
"""
@spec nif_compare(String.t(), String.t(), Options.t()) :: :lt | :eq | :gt
def nif_compare(string_a, string_b, %Options{} = options) do
{strength, backwards, alternate, case_first, case_level, normalization, numeric, reorder_bin} =
options_to_nif_args(options)
case Localize.Nif.nif_collation_cmp(
string_a,
string_b,
strength,
backwards,
alternate,
case_first,
case_level,
normalization,
numeric,
reorder_bin
) do
1 -> :gt
0 -> :eq
-1 -> :lt
end
end
@doc false
def options_to_nif_args(%Options{} = options) do
strength = encode_strength(options.strength)
backwards = encode_bool(options.backwards)
alternate = encode_alternate(options.alternate)
case_first = encode_case_first(options.case_first)
case_level = encode_bool(options.case_level)
normalization = encode_bool(options.normalization)
numeric = encode_bool(options.numeric)
reorder_bin = encode_reorder_codes(options.reorder)
{strength, backwards, alternate, case_first, case_level, normalization, numeric, reorder_bin}
end
@doc """
Returns whether all reorder codes in the list can be mapped to ICU values.
### Arguments
* `reorder_codes` - a list of script code atoms.
### Returns
* `true` if all codes are recognized.
* `false` if any code is unrecognized.
### Examples
iex> Localize.Collation.Nif.reorder_codes_supported?([:Grek, :Latn])
true
iex> Localize.Collation.Nif.reorder_codes_supported?([:Unknown])
false
iex> Localize.Collation.Nif.reorder_codes_supported?([])
true
"""
@spec reorder_codes_supported?([atom()]) :: boolean()
def reorder_codes_supported?(reorder_codes) do
Enum.all?(reorder_codes, &Map.has_key?(@script_codes, &1))
end
defp encode_strength(:tertiary), do: @opt_default
defp encode_strength(:primary), do: @ucol_primary
defp encode_strength(:secondary), do: @ucol_secondary
defp encode_strength(:quaternary), do: @ucol_quaternary
defp encode_strength(:identical), do: @ucol_identical
defp encode_bool(false), do: @opt_default
defp encode_bool(true), do: @ucol_on
defp encode_alternate(:non_ignorable), do: @opt_default
defp encode_alternate(:shifted), do: @ucol_shifted
defp encode_case_first(false), do: @opt_default
defp encode_case_first(:upper), do: @ucol_upper_first
defp encode_case_first(:lower), do: @ucol_lower_first
defp encode_reorder_codes([]), do: <<>>
defp encode_reorder_codes(codes) do
codes
|> Enum.map(&Map.fetch!(@script_codes, &1))
|> Enum.reduce(<<>>, fn code, acc -> acc <> <<code::big-signed-32>> end)
end
end