diff --git a/src/gd_gui.erl b/src/gd_gui.erl index 311d513..033de68 100644 --- a/src/gd_gui.erl +++ b/src/gd_gui.erl @@ -1048,37 +1048,3 @@ do_grids_mess_sig2(Request = #{"grids" := 1, wxDialog:destroy(Dialog); do_grids_mess_sig2(BadRequest, _) -> tell("Bad request: ~tp", [BadRequest]). - - - - -%%% Helpers - - -price_to_string(Pucks) -> - Gaju = 1_000_000_000_000_000_000, - H = integer_to_list(Pucks div Gaju), - R = Pucks rem Gaju, - case string:strip(lists:flatten(io_lib:format("~18..0w", [R])), right, $0) of - [] -> H; - T -> string:join([H, T], ".") - end. - -string_to_price(String) -> - case string:split(String, ".") of - [H] -> join_price(H, "0"); - [H, T] -> join_price(H, T); - _ -> {error, bad_price} - end. - -join_price(H, T) -> - try - Parts = [H, string:pad(T, 18, trailing, $0)], - Price = list_to_integer(unicode:characters_to_list(Parts)), - case Price < 0 of - false -> {ok, Price}; - true -> {error, negative_price} - end - catch - error:R -> {error, R} - end. diff --git a/src/gd_lib.erl b/src/gd_lib.erl new file mode 100644 index 0000000..1c43139 --- /dev/null +++ b/src/gd_lib.erl @@ -0,0 +1,279 @@ +%%% @doc +%%% GajuDesk Helper Functions +%%% @end + +-module(gd_lib). + +-export([format_price/1, format_price/3, format_price/4, % read_price/1, + price_to_string/1, string_to_price/1, + is_int/1]). + + +format_price(Pucks) -> + format_price(gaju, {west, $,, 3},Pucks). + + +format_price(puck, {west, Separator, Period}, Pucks) -> + western_format(Separator, Period, Pucks); +format_price(gaju, {west, Separator = $., Period}, Pucks) -> + western_format(Separator, $,, Period, all, Pucks); +format_price(gaju, {west, Separator, Period}, Pucks) -> + western_format(Separator, $., Period, all, Pucks); +format_price(Unit, jp, Pucks) -> + jp_format(Unit, all, Pucks). + + + +-spec format_price(Unit, Style, Precision, Pucks) -> Serialized + when Unit :: gaju | puck, + Style :: jp | jp_pucks | {Separator, Period}, + Precision :: all | 0..18, + Separator :: $, | $. | $_, + Period :: 3 | 4, + Pucks :: integer(), + Serialized :: string(). +%% @doc +%% A formatting function that accepts a price value in pucks as an integer, and outputs a string +%% formatted in the desired way. +%% +%% Consider, for example, a few input and output pairs: +%% ``` +%% format_price({$,, 3}, 3, 123456789123456789123456789) -> +%% "木123,456,789.123". +%% +%% format_price({$,, 3}, 6, 123456789123456789123456789) -> +%% "木123,456,789.123,456" +%% +%% format_price({$,, 3}, all, 123456789123456789123456789) -> +%% "木123.456.789,123.456.789.123.456.789" +%% +%% format_price({$_, 4}, 10, 123456789123456789123456789) -> +%% "木1_2345_6789.1234_5678_91" +%% +%% format_price(jp, 3, 123456789123456789123456789) -> +%% "1億2345万6789木12京3000兆本" +%% +%% format_price(jp, 6, 123456789123456789123456789) -> +%% "1億2345万6789木12京3456兆本" +%% +%% format_price(jp, 0, 123456789123456789123456789) -> +%% "1億2345万6789木" +%% +%% format_price(jp_pucks, all, 123456789123456789123456789) -> +%% "123秭4567垓8912京3456兆7891億2345万6789本" +%% ''' + + +format_price(puck, {west, Separator, Period}, _, Pucks) -> + western_format(Separator, Period, Pucks); +format_price(gaju, {west, Separator = $., Period}, Precision, Pucks) -> + western_format(Separator, $,, Period, Precision, Pucks); +format_price(gaju, {west, Separator, Period}, Precision, Pucks) -> + western_format(Separator, $., Period, Precision, Pucks); +format_price(Unit, jp, Precision, Pucks) -> + jp_format(Unit, Precision, Pucks). + + + + +western_format(Separator, Period, Pucks) -> + P = lists:reverse(integer_to_list(Pucks)), + [puck_mark() | separate(Separator, Period, P)]. + + +western_format(Separator, _, Period, 0, Pucks) -> + G = lists:reverse(integer_to_list(Pucks div one_gaju())), + [gaju_mark() | separate(Separator, Period, G)]; +western_format(Separator, Break, Period, Precision, Pucks) -> + G = lists:reverse(integer_to_list(Pucks div one_gaju())), + H = [gaju_mark() | separate(Separator, Period, G)], + case decimal_pucks(Precision, Pucks rem one_gaju()) of + {"", ""} -> + H; + {P, E} -> + T = lists:reverse(separate(Separator, Period, P)), + lists:flatten([H, Break, T, E]) + end. + +decimal_pucks(_, 0) -> + {"", ""}; +decimal_pucks(all, Pucks) -> + RTrailing = lists:reverse(integer_to_list(Pucks)), + {lists:reverse(lists:dropwhile(fun(C) -> C =:= $0 end, RTrailing)), ""}; +decimal_pucks(Precision, Pucks) -> + {Significant, Rest} = lists:split(min(Precision, 18), integer_to_list(Pucks)), + RTrailing = lists:reverse(Significant), + Trailing = lists:reverse(lists:dropwhile(fun(C) -> C =:= $0 end, RTrailing)), + case lists:all(fun(C) -> C =:= $0 end, Rest) of + true -> {Trailing, ""}; + false -> {Trailing, "..."} + end. + +separate(_, _, "") -> + ""; +separate(S, P, G) -> + separate(S, P, 1, G, []). + +separate(_, _, _, [H], A) -> + [H | A]; +separate(S, P, P, [H | T], A) -> + separate(S, P, 1, T, [S, H | A]); +separate(S, P, N, [H | T], A) -> + separate(S, P, N + 1, T, [H | A]). + + +jp_format(gaju, 0, Pucks) -> + G = lists:reverse(integer_to_list(Pucks div one_gaju())), + rank(gaju_mark(), G); +jp_format(gaju, all, Pucks) -> + H = jp_format(gaju, 0, Pucks), + T = jp_format(puck, all, Pucks rem one_gaju()), + H ++ T; +jp_format(gaju, Precision, Pucks) -> + H = jp_format(gaju, 0, Pucks), + T = jp_format(puck, Precision, Pucks rem one_gaju()), + H ++ T; +jp_format(puck, all, Pucks) -> + P = lists:reverse(integer_to_list(Pucks)), + case lists:all(fun(C) -> C =:= $0 end, P) of + false -> rank(puck_mark(), P); + true -> [$0, puck_mark()] + end; +jp_format(puck, Precision, Pucks) -> + Digits = min(Precision, 18), + P = lists:reverse(integer_to_list(Pucks)), + case length(P) > Digits of + true -> + [$0, puck_mark()]; + false -> + Significant = lists:nthtail(Digits, P), + Filler = tuple_to_list(erlang:make_tuple(Digits, $0)), + PuckingString = Filler ++ Significant, + case lists:all(fun(C) -> C =:= $0 end, PuckingString) of + false -> rank(puck_mark(), PuckingString); + true -> [$0, puck_mark()] + end + end. + + +rank(Symbol, [$0, $0, $0, $0 | PT]) -> + rank(jp_ranks(), PT, [Symbol]); +rank(Symbol, [P4, $0, $0, $0 | PT]) -> + rank(jp_ranks(), PT, [P4, Symbol]); +rank(Symbol, [P4, P3, $0, $0 | PT]) -> + rank(jp_ranks(), PT, [P3, P4, Symbol]); +rank(Symbol, [P4, P3, P2, $0 | PT]) -> + rank(jp_ranks(), PT, [P2, P3, P4, Symbol]); +rank(Symbol, [P4, P3, P2, P1 | PT]) -> + rank(jp_ranks(), PT, [P1, P2, P3, P4, Symbol]); +rank(Symbol, [P4]) -> + [P4, Symbol]; +rank(Symbol, [P4, P3]) -> + [P3, P4, Symbol]; +rank(Symbol, [P4, P3, P2]) -> + [P2, P3, P4, Symbol]. + +jp_ranks() -> +% "万億兆京垓秭穣溝澗正載極". + "ABCDEFG". + +rank([_ | RT], [$0, $0, $0, $0 | PT], A) -> + rank(RT, PT, A); +rank([RH | RT], [P4, $0, $0, $0 | PT], A) -> + rank(RT, PT, [P4, RH | A]); +rank([RH | RT], [P4, P3, $0, $0 | PT], A) -> + rank(RT, PT, [P3, P4, RH | A]); +rank([RH | RT], [P4, P3, P2, $0 | PT], A) -> + rank(RT, PT, [P2, P3, P4, RH | A]); +rank([RH | RT], [P4, P3, P2, P1 | PT], A) -> + rank(RT, PT, [P1, P2, P3, P4, RH | A]); +rank(_, [$0, $0, $0, $0], A) -> + A; +rank(_, [$0, $0, $0], A) -> + A; +rank(_, [$0, $0], A) -> + A; +rank(_, [$0], A) -> + A; +rank(_, [], A) -> + A; +rank([RH | _], [P4, $0, $0, $0], A) -> + [P4, RH | A]; +rank([RH | _], [P4, $0, $0], A) -> + [P4, RH | A]; +rank([RH | _], [P4, $0], A) -> + [P4, RH | A]; +rank([RH | _], [P4], A) -> + [P4, RH | A]; +rank([RH | _], [P4, P3, $0, $0], A) -> + [P3, P4, RH | A]; +rank([RH | _], [P4, P3, $0], A) -> + [P3, P4, RH | A]; +rank([RH | _], [P4, P3], A) -> + [P3, P4, RH | A]; +rank([RH | _], [P4, P3, P2, $0], A) -> + [P2, P3, P4, RH | A]; +rank([RH | _], [P4, P3, P2], A) -> + [P2, P3, P4, RH | A]; +rank([RH | _], [P4, P3, P2, P1], A) -> + [P1, P2, P3, P4, RH | A]. + + +gaju_mark() -> $G. + +puck_mark() -> $P. + +one_gaju() -> 1_000_000_000_000_000_000. + + +-spec price_to_string(Pucks) -> Gajus + when Pucks :: integer(), + Gajus :: string(). +%% @doc +%% A simplified formatting function that converts an integer value in Pucks to a string representation +%% in Gajus. Useful for formatting generic output for UI elements + +price_to_string(Pucks) -> + Gaju = 1_000_000_000_000_000_000, + H = integer_to_list(Pucks div Gaju), + R = Pucks rem Gaju, + case string:strip(lists:flatten(io_lib:format("~18..0w", [R])), right, $0) of + [] -> H; + T -> string:join([H, T], ".") + end. + + +-spec string_to_price(Gajus) -> Pucks + when Gajus :: string(), + Pucks :: integer(). +%% @doc +%% A simplified formatting function that converts a Gaju value represented as a string to an +%% integer value in Pucks. + +string_to_price(String) -> + case string:split(String, ".") of + [H] -> join_price(H, "0"); + [H, T] -> join_price(H, T); + _ -> {error, bad_price} + end. + +join_price(H, T) -> + try + Parts = [H, string:pad(T, 18, trailing, $0)], + Price = list_to_integer(unicode:characters_to_list(Parts)), + case Price < 0 of + false -> {ok, Price}; + true -> {error, negative_price} + end + catch + error:R -> {error, R} + end. + + +-spec is_int(string()) -> boolean(). +%% @doc +%% A simple boolean check over whether every character in a string is part of an integer value +%% without the trashy `try .. catch' way. + +is_int(S) -> + lists:all(fun(C) -> $0 =< C andalso C =< $9 end, S). diff --git a/src/gd_m_spend.erl b/src/gd_m_spend.erl index 969f293..5040444 100644 --- a/src/gd_m_spend.erl +++ b/src/gd_m_spend.erl @@ -17,6 +17,7 @@ -include_lib("wx/include/wx.hrl"). -include("$zx_include/zx_logger.hrl"). +-include("gd.hrl"). -record(s, {frame = none :: none | wx:wx_object(), @@ -232,7 +233,7 @@ done(#s{frame = Frame, caller = Caller, case clean_spend(TX) of {ok, CleanTX} -> - + end, Result = case wxTextCtrl:getValue(NameTx) of "" -> @@ -246,10 +247,12 @@ done(#s{frame = Frame, caller = Caller, ok = wxFrame:destroy(Frame), zxw_modal:done(Caller, Result). +clean_spend(TX) -> + clean_spend(TX, []). -clean_spend(#spend_tx{recipient_id = ""}) -> - ok; -clean_spend( TX = #spend_tx{amount = S}) when is_list(S) -> +clean_spend(TX = #spend_tx{recipient_id = ""}, Errors) -> + clean_spend(TX#spend_tx{recipient_id = none}, [recipient_id | Errors]); +clean_spend(TX = #spend_tx{amount = S}, Errors) when is_list(S) -> case string_to_price(S) of {ok, Amount} -> clean_spend(TX#spend_tx{amount = Amount}); {error, _} -> ok @@ -270,5 +273,3 @@ clean_spend(TX) -> {ok, CleanTX}. -is_int(S) -> - lists:all(fun(C) -> $0 =< C andalso C =< $9 end, S).