-module(gliberapay). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([download_patrons_csv_request/1, parse_patrons_csv/1]). -export_type([patron/0, error/0, date/0]). -type patron() :: {patron, date(), integer(), binary(), gleam@option:option(binary()), binary(), gleam@option:option(float()), binary()}. -type error() :: {invalid_csv_syntax, binary()} | missing_csv_header_row | {missing_csv_headers, list(binary())} | {invalid_value, binary()}. -type date() :: {date, integer(), integer(), integer()}. -spec download_patrons_csv_request(binary()) -> gleam@http@request:request(binary()). download_patrons_csv_request(Recipient) -> _pipe = gleam@http@request:new(), _pipe@1 = gleam@http@request:set_host(_pipe, <<"liberapay.com"/utf8>>), gleam@http@request:set_path( _pipe@1, <<<<"/"/utf8, Recipient/binary>>/binary, "/patrons/public.csv"/utf8>> ). -spec optional_string(binary()) -> gleam@option:option(binary()). optional_string(S) -> case S of <<""/utf8>> -> none; _ -> {some, S} end. -spec parse_int(binary()) -> {ok, integer()} | {error, error()}. parse_int(Int) -> case gleam@int:parse(Int) of {ok, I} -> {ok, I}; _ -> {error, {invalid_value, <<"invalid int: "/utf8, Int/binary>>}} end. -spec parse_amount(binary()) -> {ok, gleam@option:option(float())} | {error, error()}. parse_amount(Amount) -> case Amount of <<""/utf8>> -> {ok, none}; <<"private"/utf8>> -> {ok, none}; _ -> case gleam@float:parse(Amount) of {ok, F} -> {ok, {some, F}}; _ -> {error, {invalid_value, <<"invalid float: "/utf8, Amount/binary>>}} end end. -spec parse_date(binary()) -> {ok, date()} | {error, error()}. parse_date(Date) -> gleam@result:'try'( begin _pipe = gleam@string:split(Date, <<"-"/utf8>>), _pipe@1 = gleam@list:try_map(_pipe, fun gleam@int:parse/1), gleam@result:map_error( _pipe@1, fun(_) -> {invalid_value, <<"invalid date: "/utf8, Date/binary>>} end ) end, fun(Numbers) -> case Numbers of [Year, Month, Day] -> {ok, {date, Year, Month, Day}}; _ -> {error, {invalid_value, <<"invalid date: "/utf8, Date/binary>>}} end end ). -spec pop_headers(list(list(binary()))) -> {ok, {list(binary()), list(list(binary()))}} | {error, error()}. pop_headers(Csv) -> gleam@result:'try'(case Csv of [First | Rest] -> {ok, {First, Rest}}; [] -> {error, missing_csv_header_row} end, fun(_use0) -> {Headers, Rest@1} = _use0, Headers_set = gleam@set:from_list(Headers), Missing = gleam@list:filter( [<<"pledge_date"/utf8>>, <<"patron_id"/utf8>>, <<"patron_username"/utf8>>, <<"patron_public_name"/utf8>>, <<"donation_currency"/utf8>>, <<"weekly_amount"/utf8>>, <<"patron_avatar_url"/utf8>>], fun(H) -> not gleam@set:contains(Headers_set, H) end ), case Missing of [] -> {ok, {Headers, Rest@1}}; _ -> {error, {missing_csv_headers, Missing}} end end). -spec extract_csv_patrons(list(list(binary()))) -> {ok, list(patron())} | {error, error()}. extract_csv_patrons(Csv) -> gleam@result:'try'( pop_headers(Csv), fun(_use0) -> {Headers, Rows} = _use0, Rows@1 = gleam@list:map( Rows, fun(Row) -> _pipe = gleam@list:zip(Headers, Row), maps:from_list(_pipe) end ), gleam@list:try_map( Rows@1, fun(Row@1) -> Get = fun(K) -> case gleam@dict:get(Row@1, K) of {ok, V} -> {ok, V}; _ -> {error, {invalid_value, <<"missing value for "/utf8, K/binary>>}} end end, gleam@result:'try'( Get(<<"pledge_date"/utf8>>), fun(Date) -> gleam@result:'try'( Get(<<"patron_id"/utf8>>), fun(Id) -> gleam@result:'try'( Get(<<"patron_username"/utf8>>), fun(Username) -> gleam@result:'try'( Get( <<"patron_public_name"/utf8>> ), fun(Name) -> gleam@result:'try'( Get( <<"donation_currency"/utf8>> ), fun(Currency) -> gleam@result:'try'( Get( <<"weekly_amount"/utf8>> ), fun(Amount) -> gleam@result:'try'( Get( <<"patron_avatar_url"/utf8>> ), fun( Avatar ) -> gleam@result:'try'( parse_date( Date ), fun( Date@1 ) -> gleam@result:'try'( parse_int( Id ), fun( Id@1 ) -> gleam@result:'try'( parse_amount( Amount ), fun( Amount@1 ) -> {ok, {patron, Date@1, Id@1, Username, optional_string( Name ), Currency, Amount@1, Avatar}} end ) end ) end ) end ) end ) end ) end ) end ) end ) end ) end ) end ). -spec parse_patrons_csv(binary()) -> {ok, list(patron())} | {error, error()}. parse_patrons_csv(Csv) -> _pipe = Csv, _pipe@1 = gsv:to_lists_or_error(_pipe), _pipe@2 = gleam@result:map_error( _pipe@1, fun(Field@0) -> {invalid_csv_syntax, Field@0} end ), gleam@result:'try'(_pipe@2, fun extract_csv_patrons/1).