-module(humanize). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/humanize.gleam"). -export([number_with/3, number/1, number_with_opts/2, get_locale_data/1, make_locale/7, get_locale_data_with/2, time_ago_unix/5, time_ago_unix_with_overrides/6, time_ago_millis/5, time_ago_timestamp/5, time_ago_timestamp_with_overrides/6, time_ago_millis_with_overrides/6, bytes_decimal/1, bytes_binary/1, percent_with/4, percent/1, percent_ratio/4, duration/1, duration_precise/1, list_locale/3, list/1, list_with_oxford/1, list_with_ampersand/1, ordinal/1]). -export_type([decimal_separator/0, number_opts/0, locale_data/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type decimal_separator() :: dot | comma. -type number_opts() :: {number_opts, integer(), decimal_separator()}. -type locale_data() :: {locale_data, binary(), binary(), binary(), binary(), boolean(), list({integer(), binary(), binary()})}. -file("src/humanize.gleam", 20). -spec decimal_sep_from_sep(decimal_separator()) -> binary(). decimal_sep_from_sep(Sep) -> case Sep of dot -> <<"."/utf8>>; comma -> <<","/utf8>> end. -file("src/humanize.gleam", 28). -spec pow10_acc(integer(), integer()) -> integer(). pow10_acc(N, Acc) -> case N =< 0 of true -> Acc; false -> pow10_acc(N - 1, Acc * 10) end. -file("src/humanize.gleam", 35). -spec pow10(integer()) -> integer(). pow10(N) -> pow10_acc(N, 1). -file("src/humanize.gleam", 168). -spec pow_int_acc(integer(), integer(), integer()) -> integer(). pow_int_acc(Base, Exp, Acc) -> case Exp =< 0 of true -> Acc; false -> pow_int_acc(Base, Exp - 1, Acc * Base) end. -file("src/humanize.gleam", 175). -spec pow_int(integer(), integer()) -> integer(). pow_int(Base, Exp) -> pow_int_acc(Base, Exp, 1). -file("src/humanize.gleam", 183). -spec join_strings(list(binary()), binary()) -> binary(). join_strings(Xs, Sep) -> case Xs of [] -> <<""/utf8>>; [A] -> A; [A@1 | Rest] -> gleam@list:fold( Rest, A@1, fun(Acc, X) -> <<<>/binary, X/binary>> end ) end. -file("src/humanize.gleam", 179). -spec repeat_string(integer(), binary()) -> binary(). repeat_string(N, S) -> join_strings(gleam@list:repeat(S, N), <<""/utf8>>). -file("src/humanize.gleam", 46). ?DOC(" Format an integer using a specific number of decimals and decimal separator.\n"). -spec number_with(integer(), integer(), decimal_separator()) -> binary(). number_with(N, Decimals, Sep) -> Abs = case N < 0 of true -> - N; false -> N end, Sign = case N < 0 of true -> <<"-"/utf8>>; false -> <<""/utf8>> end, Format_scaled = fun(N@1, Denom, Suffix, Decimals@1, Sep@1, Sign@1) -> Mul = pow10(Decimals@1), Scaled = case Denom of 0 -> 0; Gleam@denominator -> ((N@1 * Mul) + (Denom div 2)) div Gleam@denominator end, Whole = case Mul of 0 -> 0; Gleam@denominator@1 -> Scaled div Gleam@denominator@1 end, Frac = case Mul of 0 -> 0; Gleam@denominator@2 -> Scaled rem Gleam@denominator@2 end, case Decimals@1 =:= 0 of true -> <<<>/binary, Suffix/binary>>; false -> Frac_s = erlang:integer_to_binary(Frac), Frac_len = string:length(Frac_s), Frac_padded = case Frac_len < Decimals@1 of true -> Needed = Decimals@1 - Frac_len, Zeros = repeat_string(Needed, <<"0"/utf8>>), <>; false -> Frac_s end, <<<<<<<>/binary, (decimal_sep_from_sep(Sep@1))/binary>>/binary, Frac_padded/binary>>/binary, Suffix/binary>> end end, case Abs >= 1000000000000 of true -> Format_scaled(Abs, 1000000000000, <<"T"/utf8>>, Decimals, Sep, Sign); false -> case Abs >= 1000000000 of true -> Mul@1 = pow10(Decimals), Scaled@1 = ((Abs * Mul@1) + (1000000000 div 2)) div 1000000000, case Scaled@1 >= (1000 * Mul@1) of true -> Format_scaled( Abs, 1000000000000, <<"T"/utf8>>, Decimals, Sep, Sign ); false -> Format_scaled( Abs, 1000000000, <<"B"/utf8>>, Decimals, Sep, Sign ) end; false -> case Abs >= 1000000 of true -> Mul@2 = pow10(Decimals), Scaled@2 = ((Abs * Mul@2) + (1000000 div 2)) div 1000000, case Scaled@2 >= (1000 * Mul@2) of true -> Format_scaled( Abs, 1000000000, <<"B"/utf8>>, Decimals, Sep, Sign ); false -> Format_scaled( Abs, 1000000, <<"M"/utf8>>, Decimals, Sep, Sign ) end; false -> case Abs >= 1000 of true -> Mul@3 = pow10(Decimals), Scaled@3 = ((Abs * Mul@3) + (1000 div 2)) div 1000, case Scaled@3 >= (1000 * Mul@3) of true -> Format_scaled( Abs, 1000000, <<"M"/utf8>>, Decimals, Sep, Sign ); false -> Format_scaled( Abs, 1000, <<"K"/utf8>>, Decimals, Sep, Sign ) end; false -> erlang:integer_to_binary(N) end end end end. -file("src/humanize.gleam", 41). ?DOC( " Format an integer into a compact human-friendly string.\n" " Example: `1234` -> \"1.2K\".\n" ). -spec number(integer()) -> binary(). number(N) -> number_with(N, 1, dot). -file("src/humanize.gleam", 139). ?DOC(" Format an integer using a `NumberOpts` record.\n"). -spec number_with_opts(integer(), number_opts()) -> binary(). number_with_opts(N, Opts) -> case Opts of {number_opts, Decimals, Sep} -> number_with(N, Decimals, Sep) end. -file("src/humanize.gleam", 195). -spec join_with_conj(list(binary()), binary()) -> binary(). join_with_conj(Items, Conj_full) -> case Items of [] -> <<""/utf8>>; [A] -> A; [A@1, B] -> <<<>/binary, B/binary>>; _ -> Reversed = lists:reverse(Items), case Reversed of [] -> <<""/utf8>>; [Last | Init_rev] -> Init = lists:reverse(Init_rev), <<<<(join_strings(Init, <<", "/utf8>>))/binary, Conj_full/binary>>/binary, Last/binary>> end end. -file("src/humanize.gleam", 252). -spec raw_to_locale_data( {binary(), binary(), binary(), binary(), boolean(), list({integer(), binary(), binary()})} ) -> locale_data(). raw_to_locale_data(Raw) -> case Raw of {Ago, In_, Nowv, Conj, Ago_prefix, Units} -> {locale_data, Ago, In_, Nowv, Conj, Ago_prefix, Units} end. -file("src/humanize.gleam", 261). -spec find_locale_raw( binary(), list({binary(), {binary(), binary(), binary(), binary(), boolean(), list({integer(), binary(), binary()})}}) ) -> locale_data(). find_locale_raw(Locale, List) -> case List of [] -> raw_to_locale_data(internals@locales:default_raw()); [{K, Raw} | Rest] -> case K =:= Locale of true -> raw_to_locale_data(Raw); false -> find_locale_raw(Locale, Rest) end end. -file("src/humanize.gleam", 282). ?DOC(" Retrieve the built-in `LocaleData` for a given locale code.\n"). -spec get_locale_data(binary()) -> locale_data(). get_locale_data(Locale) -> find_locale_raw(Locale, internals@locales:locales_raw()). -file("src/humanize.gleam", 291). ?DOC( " Create a custom `LocaleData` entry.\n" "\n" " When `ago_prefix` is `True`, the \"ago\" word is placed **before** the\n" " time core (e.g. FR \"il y a 2 heures\"). When `False` it goes **after**\n" " (e.g. EN \"1 hour ago\").\n" ). -spec make_locale( binary(), binary(), binary(), binary(), binary(), boolean(), list({integer(), binary(), binary()}) ) -> {binary(), locale_data()}. make_locale(Code, Ago, In_, Nowv, Conj, Ago_prefix, Units) -> {Code, {locale_data, Ago, In_, Nowv, Conj, Ago_prefix, Units}}. -file("src/humanize.gleam", 304). ?DOC(" Lookup `LocaleData` with custom overrides.\n"). -spec get_locale_data_with(binary(), list({binary(), locale_data()})) -> locale_data(). get_locale_data_with(Locale, Overrides) -> case Overrides of [] -> find_locale_raw(Locale, internals@locales:locales_raw()); [{K, D} | Rest] -> case K =:= Locale of true -> D; false -> get_locale_data_with(Locale, Rest) end end. -file("src/humanize.gleam", 320). -spec format_unit(integer(), binary(), binary()) -> binary(). format_unit(Count, Singular, Plural) -> case Count =:= 1 of true -> <<<<(erlang:integer_to_binary(Count))/binary, " "/utf8>>/binary, Singular/binary>>; false -> <<<<(erlang:integer_to_binary(Count))/binary, " "/utf8>>/binary, Plural/binary>> end. -file("src/humanize.gleam", 213). -spec build_parts(integer(), list({integer(), binary(), binary()}), integer()) -> list(binary()). build_parts(Rem, Us, Remaining) -> case Remaining =< 0 of true -> []; false -> case Us of [] -> []; [{Sec, Sing, Plur} | Rest] -> case case Sec of 0 -> 0; Gleam@denominator -> Rem div Gleam@denominator end of 0 -> build_parts(Rem, Rest, Remaining); N -> Part = format_unit(N, Sing, Plur), New_rem = Rem - (N * Sec), lists:append( [Part], build_parts(New_rem, Rest, Remaining - 1) ) end end end. -file("src/humanize.gleam", 328). -spec time_ago_from_data( locale_data(), integer(), integer(), boolean(), integer() ) -> binary(). time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units) -> Delta = Now_seconds - Past_seconds, Abs_delta = case Delta < 0 of true -> - Delta; false -> Delta end, Now_str = case Data of {locale_data, _, _, Nowv, _, _, _} -> Nowv end, case Abs_delta =< 5 of true -> Now_str; false -> Units = case Data of {locale_data, _, _, _, _, _, U} -> U end, Needed_units = case Precise of true -> Max_units; false -> 1 end, Parts = build_parts(Abs_delta, Units, Needed_units), Core = join_with_conj(Parts, erlang:element(5, Data)), case Delta < 0 of true -> <<<<(erlang:element(3, Data))/binary, " "/utf8>>/binary, Core/binary>>; false -> case erlang:element(6, Data) of true -> <<<<(erlang:element(2, Data))/binary, " "/utf8>>/binary, Core/binary>>; false -> <<<>/binary, (erlang:element(2, Data))/binary>> end end end. -file("src/humanize.gleam", 376). ?DOC(" Format a Unix timestamp relative to `now_seconds` using built-in locales.\n"). -spec time_ago_unix(integer(), integer(), binary(), boolean(), integer()) -> binary(). time_ago_unix(Past_seconds, Now_seconds, Locale, Precise, Max_units) -> Data = get_locale_data(Locale), time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units). -file("src/humanize.gleam", 388). ?DOC(" Like `time_ago_unix` but accepts a list of runtime overrides.\n"). -spec time_ago_unix_with_overrides( integer(), integer(), binary(), list({binary(), locale_data()}), boolean(), integer() ) -> binary(). time_ago_unix_with_overrides( Past_seconds, Now_seconds, Locale, Overrides, Precise, Max_units ) -> Data = get_locale_data_with(Locale, Overrides), time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units). -file("src/humanize.gleam", 401). ?DOC(" Convenience wrapper: accept timestamps in milliseconds instead of seconds.\n"). -spec time_ago_millis(integer(), integer(), binary(), boolean(), integer()) -> binary(). time_ago_millis(Past_millis, Now_millis, Locale, Precise, Max_units) -> Past = Past_millis div 1000, Now = Now_millis div 1000, time_ago_unix(Past, Now, Locale, Precise, Max_units). -file("src/humanize.gleam", 423). ?DOC( " Format two `gleam_time` `Timestamp` values as a relative-time string.\n" " This is the modern alternative to `time_ago_unix` when working with\n" " `gleam_time/timestamp.system_time()` or parsed RFC-3339 timestamps.\n" "\n" " ```gleam\n" " import gleam/time/timestamp\n" "\n" " humanize.time_ago_timestamp(past_ts, timestamp.system_time(), \"fr\", False, 1)\n" " // -> \"il y a 2 heures\"\n" " ```\n" ). -spec time_ago_timestamp( gleam@time@timestamp:timestamp(), gleam@time@timestamp:timestamp(), binary(), boolean(), integer() ) -> binary(). time_ago_timestamp(Past, Now, Locale, Precise, Max_units) -> {Past_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Past), {Now_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Now), time_ago_unix(Past_s, Now_s, Locale, Precise, Max_units). -file("src/humanize.gleam", 436). ?DOC(" Like `time_ago_timestamp` but accepts runtime locale overrides.\n"). -spec time_ago_timestamp_with_overrides( gleam@time@timestamp:timestamp(), gleam@time@timestamp:timestamp(), binary(), list({binary(), locale_data()}), boolean(), integer() ) -> binary(). time_ago_timestamp_with_overrides( Past, Now, Locale, Overrides, Precise, Max_units ) -> {Past_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Past), {Now_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Now), time_ago_unix_with_overrides( Past_s, Now_s, Locale, Overrides, Precise, Max_units ). -file("src/humanize.gleam", 457). ?DOC(" Millisecond-based wrapper with overrides.\n"). -spec time_ago_millis_with_overrides( integer(), integer(), binary(), list({binary(), locale_data()}), boolean(), integer() ) -> binary(). time_ago_millis_with_overrides( Past_millis, Now_millis, Locale, Overrides, Precise, Max_units ) -> Past = Past_millis div 1000, Now = Now_millis div 1000, time_ago_unix_with_overrides( Past, Now, Locale, Overrides, Precise, Max_units ). -file("src/humanize.gleam", 470). -spec find_denom_for(integer(), integer(), list(binary()), integer()) -> {integer(), binary(), list(binary())}. find_denom_for(N, Base, Units, Idx) -> case Units of [] -> {1, <<"B"/utf8>>, []}; [U] -> {pow_int(Base, Idx), U, []}; [U@1 | Rest] -> Denom = pow_int(Base, Idx), Next = Denom * Base, case N < Next of true -> {Denom, U@1, Rest}; false -> find_denom_for(N, Base, Rest, Idx + 1) end end. -file("src/humanize.gleam", 493). -spec promote_if_needed_bytes( integer(), integer(), integer(), integer(), binary(), list(binary()) ) -> {integer(), binary(), list(binary())}. promote_if_needed_bytes(N, Base, Mul, Denom, Unit, Rest) -> Scaled = case Denom of 0 -> 0; Gleam@denominator -> ((N * Mul) + (Denom div 2)) div Gleam@denominator end, case Scaled >= (Base * Mul) of true -> case Rest of [] -> {Denom, Unit, Rest}; [Next | Rest2] -> {Denom * Base, Next, Rest2} end; false -> {Denom, Unit, Rest} end. -file("src/humanize.gleam", 513). -spec bytes_with( integer(), integer(), list(binary()), integer(), decimal_separator() ) -> binary(). bytes_with(Bytes, Base, Units, Decimals, Sep) -> N = case Bytes < 0 of true -> - Bytes; false -> Bytes end, Sign = case Bytes < 0 of true -> <<"-"/utf8>>; false -> <<""/utf8>> end, {Denom, Unit, Rest} = find_denom_for(N, Base, Units, 0), Mul = pow10(Decimals), {Denom@1, Unit@1, _} = promote_if_needed_bytes( N, Base, Mul, Denom, Unit, Rest ), Scaled = case Denom@1 of 0 -> 0; Gleam@denominator -> ((N * Mul) + (Denom@1 div 2)) div Gleam@denominator end, Whole = case Mul of 0 -> 0; Gleam@denominator@1 -> Scaled div Gleam@denominator@1 end, Frac = case Mul of 0 -> 0; Gleam@denominator@2 -> Scaled rem Gleam@denominator@2 end, case (Decimals =:= 0) orelse (Denom@1 =:= 1) of true -> <<<<<>/binary, " "/utf8>>/binary, Unit@1/binary>>; false -> Frac_s = erlang:integer_to_binary(Frac), Frac_len = string:length(Frac_s), Frac_padded = case Frac_len < Decimals of true -> Needed = Decimals - Frac_len, Zeros = repeat_string(Needed, <<"0"/utf8>>), <>; false -> Frac_s end, <<<<<<<<<>/binary, (decimal_sep_from_sep(Sep))/binary>>/binary, Frac_padded/binary>>/binary, " "/utf8>>/binary, Unit@1/binary>> end. -file("src/humanize.gleam", 146). ?DOC(" Format bytes using decimal units (base 1000), supports up to Yottabyte (YB).\n"). -spec bytes_decimal(integer()) -> binary(). bytes_decimal(Bytes) -> bytes_with( Bytes, 1000, [<<"B"/utf8>>, <<"KB"/utf8>>, <<"MB"/utf8>>, <<"GB"/utf8>>, <<"TB"/utf8>>, <<"PB"/utf8>>, <<"EB"/utf8>>, <<"ZB"/utf8>>, <<"YB"/utf8>>], 1, dot ). -file("src/humanize.gleam", 157). ?DOC(" Format bytes using binary units (base 1024), supports up to Yobibyte (YiB).\n"). -spec bytes_binary(integer()) -> binary(). bytes_binary(Bytes) -> bytes_with( Bytes, 1024, [<<"B"/utf8>>, <<"KiB"/utf8>>, <<"MiB"/utf8>>, <<"GiB"/utf8>>, <<"TiB"/utf8>>, <<"PiB"/utf8>>, <<"EiB"/utf8>>, <<"ZiB"/utf8>>, <<"YiB"/utf8>>], 1, dot ). -file("src/humanize.gleam", 573). ?DOC(" Format a percentage value with custom options.\n"). -spec percent_with(integer(), integer(), decimal_separator(), boolean()) -> binary(). percent_with(N, Decimals, Sep, Spaced) -> Formatted = case Decimals =:= 0 of true -> erlang:integer_to_binary(N); false -> Zeros = repeat_string(Decimals, <<"0"/utf8>>), <<<<(erlang:integer_to_binary(N))/binary, (decimal_sep_from_sep(Sep))/binary>>/binary, Zeros/binary>> end, case Spaced of true -> <>; false -> <> end. -file("src/humanize.gleam", 568). ?DOC(" Format an integer as a percentage string. Uses no decimals by default.\n"). -spec percent(integer()) -> binary(). percent(N) -> percent_with(N, 0, dot, false). -file("src/humanize.gleam", 595). ?DOC(" Compute and format a ratio as a percentage with rounding.\n"). -spec percent_ratio(integer(), integer(), integer(), decimal_separator()) -> binary(). percent_ratio(Numerator, Denominator, Decimals, Sep) -> case Denominator =:= 0 of true -> <<"NaN%"/utf8>>; false -> Negative_num = Numerator < 0, Negative_den = Denominator < 0, Sign = case Negative_num /= Negative_den of true -> <<"-"/utf8>>; false -> <<""/utf8>> end, N = case Numerator < 0 of true -> - Numerator; false -> Numerator end, D = case Denominator < 0 of true -> - Denominator; false -> Denominator end, Mul = pow10(Decimals), Scaled = case D of 0 -> 0; Gleam@denominator -> (((N * 100) * Mul) + (D div 2)) div Gleam@denominator end, Whole = case Mul of 0 -> 0; Gleam@denominator@1 -> Scaled div Gleam@denominator@1 end, Frac = case Mul of 0 -> 0; Gleam@denominator@2 -> Scaled rem Gleam@denominator@2 end, case Decimals =:= 0 of true -> <<<>/binary, "%"/utf8>>; false -> Frac_s = erlang:integer_to_binary(Frac), Frac_len = string:length(Frac_s), Frac_padded = case Frac_len < Decimals of true -> Needed = Decimals - Frac_len, Zeros = repeat_string(Needed, <<"0"/utf8>>), <>; false -> Frac_s end, <<<<<<<>/binary, (decimal_sep_from_sep(Sep))/binary>>/binary, Frac_padded/binary>>/binary, "%"/utf8>> end end. -file("src/humanize.gleam", 655). ?DOC( " Format a duration in seconds into a single largest unit description.\n" " Negative values are formatted with a leading \"-\".\n" ). -spec duration(integer()) -> binary(). duration(Seconds) -> Abs = case Seconds < 0 of true -> - Seconds; false -> Seconds end, Sign = case Seconds < 0 of true -> <<"-"/utf8>>; false -> <<""/utf8>> end, Body = case Abs < 60 of true -> case Abs =:= 1 of true -> <<"1 second"/utf8>>; false -> <<(erlang:integer_to_binary(Abs))/binary, " seconds"/utf8>> end; false -> case Abs < 3600 of true -> M = Abs div 60, case M =:= 1 of true -> <<"1 minute"/utf8>>; false -> <<(erlang:integer_to_binary(M))/binary, " minutes"/utf8>> end; false -> case Abs < 86400 of true -> H = Abs div 3600, case H =:= 1 of true -> <<"1 hour"/utf8>>; false -> <<(erlang:integer_to_binary(H))/binary, " hours"/utf8>> end; false -> D = Abs div 86400, case D =:= 1 of true -> <<"1 day"/utf8>>; false -> <<(erlang:integer_to_binary(D))/binary, " days"/utf8>> end end end end, <>. -file("src/humanize.gleam", 706). ?DOC( " Precise duration with multiple units, joined by commas.\n" " Negative values are formatted with a leading \"-\".\n" ). -spec duration_precise(integer()) -> binary(). duration_precise(Seconds) -> Abs = case Seconds < 0 of true -> - Seconds; false -> Seconds end, Sign = case Seconds < 0 of true -> <<"-"/utf8>>; false -> <<""/utf8>> end, D = Abs div 86400, H = (Abs rem 86400) div 3600, M = (Abs rem 3600) div 60, S = Abs rem 60, Parts = [], Parts@1 = case D > 0 of true -> case D =:= 1 of true -> lists:append(Parts, [<<"1 day"/utf8>>]); false -> lists:append( Parts, [<<(erlang:integer_to_binary(D))/binary, " days"/utf8>>] ) end; false -> Parts end, Parts@2 = case H > 0 of true -> case H =:= 1 of true -> lists:append(Parts@1, [<<"1 hour"/utf8>>]); false -> lists:append( Parts@1, [<<(erlang:integer_to_binary(H))/binary, " hours"/utf8>>] ) end; false -> Parts@1 end, Parts@3 = case M > 0 of true -> case M =:= 1 of true -> lists:append(Parts@2, [<<"1 minute"/utf8>>]); false -> lists:append( Parts@2, [<<(erlang:integer_to_binary(M))/binary, " minutes"/utf8>>] ) end; false -> Parts@2 end, Parts@4 = case S > 0 of true -> case S =:= 1 of true -> lists:append(Parts@3, [<<"1 second"/utf8>>]); false -> lists:append( Parts@3, [<<(erlang:integer_to_binary(S))/binary, " seconds"/utf8>>] ) end; false -> Parts@3 end, case Parts@4 of [] -> <<"0 seconds"/utf8>>; _ -> <>))/binary>> end. -file("src/humanize.gleam", 779). ?DOC(" Join a list with a custom conjunction and Oxford comma option.\n"). -spec list_locale(list(binary()), binary(), boolean()) -> binary(). list_locale(Items, Conj, Oxford) -> Resolved_conj = case Conj /= <<""/utf8>> of true -> Conj; false -> case Oxford of true -> <<", and "/utf8>>; false -> <<" and "/utf8>> end end, Pair_conj = case Conj /= <<""/utf8>> of true -> Conj; false -> <<" and "/utf8>> end, case Items of [] -> <<""/utf8>>; [A] -> A; [A@1, B] -> <<<>/binary, B/binary>>; _ -> join_with_conj(Items, Resolved_conj) end. -file("src/humanize.gleam", 763). ?DOC(" Join a list of strings into a human-readable sentence fragment.\n"). -spec list(list(binary())) -> binary(). list(Items) -> list_locale(Items, <<""/utf8>>, false). -file("src/humanize.gleam", 768). ?DOC(" Join a list using the Oxford comma when appropriate.\n"). -spec list_with_oxford(list(binary())) -> binary(). list_with_oxford(Items) -> list_locale(Items, <<""/utf8>>, true). -file("src/humanize.gleam", 774). ?DOC(" Join a list using an ampersand as the final conjunction.\n"). -spec list_with_ampersand(list(binary())) -> binary(). list_with_ampersand(Items) -> list_locale(Items, <<" & "/utf8>>, false). -file("src/humanize.gleam", 802). ?DOC(" Return the English ordinal suffix for an integer (e.g., 1st, 2nd).\n"). -spec ordinal(integer()) -> binary(). ordinal(N) -> Abs = case N < 0 of true -> - N; false -> N end, Mod100 = Abs rem 100, Mod10 = Abs rem 10, Suffix = case Mod100 of 11 -> <<"th"/utf8>>; 12 -> <<"th"/utf8>>; 13 -> <<"th"/utf8>>; _ -> case Mod10 of 1 -> <<"st"/utf8>>; 2 -> <<"nd"/utf8>>; 3 -> <<"rd"/utf8>>; _ -> <<"th"/utf8>> end end, <<(erlang:integer_to_binary(N))/binary, Suffix/binary>>.