-module(birl@duration). -compile([no_auto_import, nowarn_unused_vars]). -export([new/1, accurate_new/1, parse/1, decompose/1, accurate_decompose/1]). -export_type([duration/0, unit/0]). -type duration() :: {duration, integer()}. -type unit() :: micro_second | milli_second | second | minute | hour | day | week | month | year. -spec new(list({integer(), unit()})) -> duration(). new(Values) -> _pipe = Values, _pipe@1 = gleam@list:fold(_pipe, 0, fun(Total, Current) -> case Current of {Amount, micro_second} -> Total + Amount; {Amount@1, milli_second} -> Total + (Amount@1 * 1000); {Amount@2, second} -> Total + (Amount@2 * 1000000); {Amount@3, minute} -> Total + (Amount@3 * 60000000); {Amount@4, hour} -> Total + (Amount@4 * 3600000000); {Amount@5, day} -> Total + (Amount@5 * 86400000000); {Amount@6, week} -> Total + (Amount@6 * 604800000000); {Amount@7, month} -> Total + (Amount@7 * 2592000000000); {Amount@8, year} -> Total + (Amount@8 * 31536000000000) end end), {duration, _pipe@1}. -spec accurate_new(list({integer(), unit()})) -> duration(). accurate_new(Values) -> _pipe = Values, _pipe@1 = gleam@list:fold(_pipe, 0, fun(Total, Current) -> case Current of {Amount, micro_second} -> Total + Amount; {Amount@1, milli_second} -> Total + (Amount@1 * 1000); {Amount@2, second} -> Total + (Amount@2 * 1000000); {Amount@3, minute} -> Total + (Amount@3 * 60000000); {Amount@4, hour} -> Total + (Amount@4 * 3600000000); {Amount@5, day} -> Total + (Amount@5 * 86400000000); {Amount@6, week} -> Total + (Amount@6 * 604800000000); {Amount@7, month} -> Total + (Amount@7 * 2629746000000); {Amount@8, year} -> Total + (Amount@8 * 31556952000000) end end), {duration, _pipe@1}. -spec parse(binary()) -> {ok, duration()} | {error, nil}. parse(Expression) -> _assert_subject = gleam@regex:from_string( <<"([+|\\-])?\\s*(\\d+)\\s*(\\w+)?"/utf8>> ), {ok, Re} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"birl/duration"/utf8>>, function => <<"parse"/utf8>>, line => 205}) end, {Constructor, Expression@2} = case gleam@string:starts_with( Expression, <<"accurate:"/utf8>> ) of true -> [_, Expression@1] = gleam@string:split(Expression, <<":"/utf8>>), {fun accurate_new/1, Expression@1}; false -> {fun new/1, Expression} end, case begin _pipe = Expression@2, _pipe@1 = gleam@string:lowercase(_pipe), _pipe@2 = gleam@regex:scan(Re, _pipe@1), gleam@list:try_map(_pipe@2, fun(Item) -> case Item of {match, _, [Sign_option, {some, Amount_string}]} -> gleam@result:then( gleam@int:parse(Amount_string), fun(Amount) -> _pipe@3 = {case Sign_option of none -> Amount; {some, <<"+"/utf8>>} -> Amount; {some, <<"-"/utf8>>} -> -1 * Amount end, micro_second}, {ok, _pipe@3} end ); {match, _, [Sign_option@1, {some, Amount_string@1}, {some, Unit}]} -> gleam@result:then( gleam@int:parse(Amount_string@1), fun(Amount@1) -> gleam@result:then( gleam@list:find( [{year, [<<"y"/utf8>>, <<"year"/utf8>>, <<"years"/utf8>>]}, {month, [<<"mon"/utf8>>, <<"month"/utf8>>, <<"months"/utf8>>]}, {week, [<<"w"/utf8>>, <<"week"/utf8>>, <<"weeks"/utf8>>]}, {day, [<<"d"/utf8>>, <<"day"/utf8>>, <<"days"/utf8>>]}, {hour, [<<"h"/utf8>>, <<"hour"/utf8>>, <<"hours"/utf8>>]}, {minute, [<<"m"/utf8>>, <<"min"/utf8>>, <<"minute"/utf8>>, <<"minutes"/utf8>>]}, {second, [<<"s"/utf8>>, <<"sec"/utf8>>, <<"secs"/utf8>>, <<"second"/utf8>>, <<"seconds"/utf8>>]}, {milli_second, [<<"ms"/utf8>>, <<"msec"/utf8>>, <<"msecs"/utf8>>, <<"millisecond"/utf8>>, <<"milliseconds"/utf8>>, <<"milli-second"/utf8>>, <<"milli-seconds"/utf8>>, <<"milli_second"/utf8>>, <<"milli_seconds"/utf8>>]}], fun(Item@1) -> gleam@list:contains( erlang:element(2, Item@1), Unit ) end ), fun(_use0) -> {Unit@1, _} = _use0, _pipe@4 = {case Sign_option@1 of none -> Amount@1; {some, <<"+"/utf8>>} -> Amount@1; {some, <<"-"/utf8>>} -> -1 * Amount@1 end, Unit@1}, {ok, _pipe@4} end ) end ); _ -> {error, nil} end end) end of {ok, Values} -> _pipe@5 = Values, _pipe@6 = Constructor(_pipe@5), {ok, _pipe@6}; {error, nil} -> {error, nil} end. -spec extract(integer(), integer()) -> {integer(), integer()}. extract(Duration, Unit) -> {case Unit of 0 -> 0; Gleam@denominator -> Duration div Gleam@denominator end, case Unit of 0 -> 0; Gleam@denominator@1 -> Duration rem Gleam@denominator@1 end}. -spec decompose(duration()) -> list({integer(), unit()}). decompose(Duration) -> {duration, Value} = Duration, Absolute_value = gleam@int:absolute_value(Value), {Years, Remaining} = extract(Absolute_value, 31536000000000), {Months, Remaining@1} = extract(Remaining, 2592000000000), {Days, Remaining@2} = extract(Remaining@1, 86400000000), {Hours, Remaining@3} = extract(Remaining@2, 3600000000), {Minutes, Remaining@4} = extract(Remaining@3, 60000000), {Seconds, Remaining@5} = extract(Remaining@4, 1000000), {Milli_seconds, Remaining@6} = extract(Remaining@5, 1000), _pipe = [{Years, year}, {Months, month}, {Days, day}, {Hours, hour}, {Minutes, minute}, {Seconds, second}, {Milli_seconds, milli_second}, {Remaining@6, micro_second}], _pipe@1 = gleam@list:filter( _pipe, fun(Item) -> erlang:element(1, Item) > 0 end ), gleam@list:map(_pipe@1, fun(Item@1) -> case Value < 0 of true -> {-1 * erlang:element(1, Item@1), erlang:element(2, Item@1)}; false -> Item@1 end end). -spec accurate_decompose(duration()) -> list({integer(), unit()}). accurate_decompose(Duration) -> {duration, Value} = Duration, Absolute_value = gleam@int:absolute_value(Value), {Years, Remaining} = extract(Absolute_value, 31556952000000), {Months, Remaining@1} = extract(Remaining, 2629746000000), {Days, Remaining@2} = extract(Remaining@1, 86400000000), {Hours, Remaining@3} = extract(Remaining@2, 3600000000), {Minutes, Remaining@4} = extract(Remaining@3, 60000000), {Seconds, Remaining@5} = extract(Remaining@4, 1000000), {Milli_seconds, Remaining@6} = extract(Remaining@5, 1000), _pipe = [{Years, year}, {Months, month}, {Days, day}, {Hours, hour}, {Minutes, minute}, {Seconds, second}, {Milli_seconds, milli_second}, {Remaining@6, micro_second}], _pipe@1 = gleam@list:filter( _pipe, fun(Item) -> erlang:element(1, Item) > 0 end ), gleam@list:map(_pipe@1, fun(Item@1) -> case Value < 0 of true -> {-1 * erlang:element(1, Item@1), erlang:element(2, Item@1)}; false -> Item@1 end end).