defmodule Cldr.Print.Format do @moduledoc false import Cldr.Print.Transform alias Cldr.Number.Format.Meta def format("d" = type, format, options) do backend = Keyword.get(options, :backend, Cldr.Print.Backend) formatter = Module.concat(backend, Number.Formatter.Decimal) meta = meta_from_format(type, format, backend, options) value = format[:value] options = maybe_set_number_system(format, backend, options) case formatter.to_string(value, meta, options) do {:error, {exception, reason}} -> raise exception, reason string -> maybe_add_padding(string, format[:width], format[:left_justify]) end end def format("i", format, options) do {_, format} = Keyword.get_and_update(format, :value, fn value -> {value, truncate(value)} end) format = Keyword.put(format, :precision, 0) format("d", format, options) end def format("f", format, options) do format = maybe_add_precision(format, format[:precision]) format("d", format, options) end def format("u", format, options) do {_, format} = Keyword.get_and_update(format, :value, fn value -> {value, absolute(value)} end) format("d", format, options) end def format("e", format, options) do format = Keyword.put(format, :exponent, true) format("d", format, options) |> String.downcase end def format("E", format, options) do format = Keyword.put(format, :exponent, true) format("d", format, options) |> String.upcase end def format("g", format, options) do format_f = format("f", format, options) format_e = format("e", format, options) if String.length(format_f) <= String.length(format_e) do format_f else format_e end end def format("G", format, options) do format_f = format("F", format, options) format_e = format("E", format, options) if String.length(format_f) <= String.length(format_e) do format_f else format_e end end def format("s", format, _options) do padding = format[:width] || 0 precision = format[:precision] left_or_right = format[:left_justify] value = format[:value] value |> slice(precision) |> justify(padding, left_or_right) end def format("o", format, options) do {_, format} = Keyword.get_and_update(format, :value, fn value -> {value, Integer.to_string(truncate(value), 8) |> String.downcase} end) format = format |> maybe_add_zero_x("0", format[:leading_zero_x], options) |> maybe_add_plus(format[:with_plus], options) format("s", format, options) end def format("x", format, options) do {_, format} = Keyword.get_and_update(format, :value, fn value -> {value, Integer.to_string(truncate(value), 16) |> String.downcase} end) format = format |> maybe_add_zero_x("0x", format[:leading_zero_x], options) |> maybe_add_plus(format[:with_plus], options) format("s", format, options) end def format("X", format, options) do {_, format} = Keyword.get_and_update(format, :value, fn value -> {value, Integer.to_string(truncate(value), 16)} end) format = format |> maybe_add_zero_x("0X", format[:leading_zero_x], options) |> maybe_add_plus(format[:with_plus], options) format("s", format, options) end def meta_from_format("d", format, backend, options) do Meta.new |> maybe_add_plus(format[:with_plus]) |> maybe_add_fraction_digits(format[:precision]) |> maybe_add_zero_fill(format, format[:left_justify], format[:zero_fill], format[:width]) |> maybe_add_group(format[:group], backend, options) |> maybe_add_exponent(format[:exponent]) end defp truncate(number) when is_integer(number) do number end defp truncate(number) when is_float(number) do trunc(number) end defp truncate(%Decimal{} = number) do Decimal.round(number, 0) end def absolute(number) when is_number(number) do abs(number) end def absolute(%Decimal{} = number) do Decimal.abs(number) end defp slice(string, nil) do string end defp slice(string, precision) do String.slice(string, 0, precision) end defp justify(string, padding, true) do String.pad_trailing(string, padding) end defp justify(string, padding, nil) do String.pad_leading(string, padding) end end