Packages
ex_cldr_units
3.20.5
3.20.5
3.20.4
3.20.3
3.20.2
3.20.1
3.20.0
3.19.2
3.19.1
3.19.0
3.18.1
3.18.0
3.17.2
3.17.1
3.17.0
3.16.5
3.16.4
3.16.3
3.16.2
3.16.1
3.16.0
3.15.0
3.14.0
3.13.3
3.13.2
3.13.1
3.13.0
3.12.2
3.12.1
3.12.0
3.11.0
3.10.0
3.9.2
3.9.1
3.9.0
3.8.0
3.8.0-rc.2
3.8.0-rc.1
3.8.0-rc.0
3.7.1
3.7.0
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.0
3.4.0-rc.0
3.3.1
3.3.0
3.3.0-rc.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.1
3.0.0
3.0.0-rc.0
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.3
2.5.2
2.5.1
2.5.0
2.4.0
2.3.3
2.3.2
retired
2.3.1
2.3.0
2.2.0
2.1.0
2.0.0
1.3.0
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
retired
1.0.1
retired
1.0.0
retired
1.0.0-rc.0
retired
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Unit formatting (volume, area, length, ...), conversion and arithmetic functions based upon the Common Locale Data Repository (CLDR).
Current section
Files
Jump to
Current section
Files
lib/cldr/unit/conversion/conversions.ex
defmodule Cldr.Unit.Conversions do
@moduledoc false
alias Cldr.Unit.Conversion
alias Cldr.Unit.Parser
# CLDR ships some conversion factors and offsets as decimal strings whose
# significant-digit count exceeds Decimal 3.0's default `:max_digits` of 34
# (e.g. exact rational expansions of imperial conversions). `Decimal.new/1`
# rejects those strings on Decimal 3.0; `Decimal.parse/2` with
# `max_digits: :infinity` accepts them and is available since Decimal 2.4.
# Older Decimal versions (1.x, 2.0–2.3) have no parse-time digit limit, so
# `Decimal.new/1` is fine there.
parse_decimal =
if function_exported?(Decimal, :parse, 2) do
fn string ->
case Decimal.parse(string, max_digits: :infinity) do
{decimal, ""} ->
decimal
_other ->
raise ArgumentError, "Could not parse Decimal: #{inspect(string)}"
end
end
else
&Decimal.new/1
end
@conversions Map.get(Cldr.Config.units(), :conversions)
|> Kernel.++(Cldr.Unit.Additional.conversions())
|> Enum.map(fn
{k, v} -> {k, struct(Conversion, v)}
end)
|> Enum.map(fn
{unit, %{special: special} = conversion} when not is_nil(special) ->
{unit, conversion}
{unit, %{factor: factor} = conversion} when is_number(factor) ->
{unit, conversion}
{unit, %{factor: factor} = conversion} when is_binary(factor) ->
{unit, %{conversion | factor: parse_decimal.(factor)}}
{unit, %{factor: factor} = conversion} ->
{unit, %{conversion | factor: Decimal.div(factor.numerator, factor.denominator)}}
end)
|> Enum.map(fn
{unit, %{special: special} = conversion} when not is_nil(special) ->
{unit, conversion}
{unit, %{offset: offset} = conversion} when is_number(offset) ->
{unit, conversion}
{unit, %{offset: offset} = conversion} when is_binary(offset) ->
{unit, %{conversion | offset: parse_decimal.(offset)}}
{unit, %{offset: offset} = conversion} ->
{unit, %{conversion | offset: Decimal.div(offset.numerator, offset.denominator)}}
end)
|> Enum.map(fn
{unit, %{base_unit: base_unit} = conversion} ->
{unit, [{unit, %{conversion | base_unit: [base_unit]}}]}
end)
|> Map.new()
@identity_conversions Enum.map(@conversions, fn
{_k, [{_v, %Conversion{base_unit: [base_unit]}}]} ->
{base_unit,
[
{base_unit,
%Conversion{base_unit: [base_unit], offset: 0, factor: 1}}
]}
end)
|> Map.new()
@all_conversions Map.merge(@conversions, @identity_conversions)
def conversions do
unquote(Macro.escape(@all_conversions))
end
def conversion_for(unit) when is_atom(unit) do
case Map.fetch(conversions(), unit) do
{:ok, conversion} ->
{:ok, conversion}
:error ->
unit_string = Atom.to_string(unit)
Parser.parse_unit(unit_string)
end
end
def conversion_for(unit) when is_binary(unit) do
unit
|> String.to_existing_atom()
|> conversion_for()
rescue
ArgumentError ->
{:error, Cldr.Unit.unit_error(unit)}
end
def conversion_for!(unit) do
case conversion_for(unit) do
{:ok, conversion} -> conversion
{:error, {exception, reason}} -> raise exception, reason
end
end
end