-module(gsv). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([from_lists/3, from_dicts/3, to_lists/1, to_dicts/1]). -export_type([parse_error/0, line_ending/0, parse_status/0]). -type parse_error() :: {unescaped_quote, integer()} | {unclosed_escaped_field, integer()}. -type line_ending() :: windows | unix. -type parse_status() :: parsing_escaped_field | parsing_unescaped_field | comma_found | newline_found. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 46). -spec line_ending_to_string(line_ending()) -> binary(). line_ending_to_string(Le) -> case Le of windows -> <<"\r\n"/utf8>>; unix -> <<"\n"/utf8>> end. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 406). -spec escape_field(binary(), binary()) -> binary(). escape_field(Field, Separator) -> case gleam_stdlib:contains_string(Field, <<"\""/utf8>>) of true -> <<<<"\""/utf8, (gleam@string:replace(Field, <<"\""/utf8>>, <<"\"\""/utf8>>))/binary>>/binary, "\""/utf8>>; false -> case gleam_stdlib:contains_string(Field, Separator) orelse gleam_stdlib:contains_string( Field, <<"\n"/utf8>> ) of true -> <<<<"\""/utf8, Field/binary>>/binary, "\""/utf8>>; false -> Field end end. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 391). -spec from_lists(list(list(binary())), binary(), line_ending()) -> binary(). from_lists(Rows, Separator, Line_ending) -> Line_ending@1 = line_ending_to_string(Line_ending), _pipe@1 = gleam@list:map( Rows, fun(Row) -> _pipe = gleam@list:map( Row, fun(_capture) -> escape_field(_capture, Separator) end ), gleam@string:join(_pipe, Separator) end ), _pipe@2 = gleam@string:join(_pipe@1, Line_ending@1), gleam@string:append(_pipe@2, Line_ending@1). -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 443). -spec row_dict_to_list(gleam@dict:dict(binary(), binary()), list(binary())) -> list(binary()). row_dict_to_list(Row, Headers) -> gleam@list:map( Headers, fun(Header) -> case gleam_stdlib:map_get(Row, Header) of {ok, Field} -> Field; {error, nil} -> <<""/utf8>> end end ). -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 423). -spec from_dicts( list(gleam@dict:dict(binary(), binary())), binary(), line_ending() ) -> binary(). from_dicts(Rows, Separator, Line_ending) -> case Rows of [] -> <<""/utf8>>; _ -> Headers = begin _pipe = Rows, _pipe@1 = gleam@list:flat_map(_pipe, fun maps:keys/1), _pipe@2 = gleam@list:unique(_pipe@1), gleam@list:sort(_pipe@2, fun gleam@string:compare/2) end, Rows@1 = gleam@list:map( Rows, fun(_capture) -> row_dict_to_list(_capture, Headers) end ), from_lists([Headers | Rows@1], Separator, Line_ending) end. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 306). -spec extract_field(binary(), integer(), integer(), parse_status()) -> binary(). extract_field(String, From, Length, Status) -> Field = gsv_ffi:slice(String, From, Length), case Status of comma_found -> Field; parsing_unescaped_field -> Field; newline_found -> Field; parsing_escaped_field -> gleam@string:replace(Field, <<"\"\""/utf8>>, <<"\""/utf8>>) end. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 141). -spec do_parse( binary(), binary(), integer(), integer(), list(binary()), list(list(binary())), parse_status() ) -> {ok, list(list(binary()))} | {error, parse_error()}. do_parse(String, Original, Field_start, Field_length, Row, Rows, Status) -> case {String, Status} of {<<","/utf8, Rest/binary>>, comma_found} -> Field = extract_field(Original, Field_start, Field_length, Status), Row@1 = [Field | Row], Field_start@1 = (Field_start + Field_length) + 1, do_parse(Rest, Original, Field_start@1, 0, Row@1, Rows, comma_found); {<<","/utf8, Rest/binary>>, newline_found} -> Field = extract_field(Original, Field_start, Field_length, Status), Row@1 = [Field | Row], Field_start@1 = (Field_start + Field_length) + 1, do_parse(Rest, Original, Field_start@1, 0, Row@1, Rows, comma_found); {<<","/utf8, Rest/binary>>, parsing_unescaped_field} -> Field = extract_field(Original, Field_start, Field_length, Status), Row@1 = [Field | Row], Field_start@1 = (Field_start + Field_length) + 1, do_parse(Rest, Original, Field_start@1, 0, Row@1, Rows, comma_found); {<<"\","/utf8, Rest@1/binary>>, parsing_escaped_field} -> Field@1 = extract_field(Original, Field_start, Field_length, Status), Row@2 = [Field@1 | Row], Field_start@2 = (Field_start + Field_length) + 2, do_parse( Rest@1, Original, Field_start@2, 0, Row@2, Rows, comma_found ); {<<""/utf8>>, parsing_unescaped_field} -> Field@2 = extract_field(Original, Field_start, Field_length, Status), Row@3 = lists:reverse([Field@2 | Row]), {ok, lists:reverse([Row@3 | Rows])}; {<<"\""/utf8>>, parsing_escaped_field} -> Field@2 = extract_field(Original, Field_start, Field_length, Status), Row@3 = lists:reverse([Field@2 | Row]), {ok, lists:reverse([Row@3 | Rows])}; {<<""/utf8>>, comma_found} -> Row@4 = lists:reverse([<<""/utf8>> | Row]), {ok, lists:reverse([Row@4 | Rows])}; {<<""/utf8>>, newline_found} -> {ok, lists:reverse(Rows)}; {<<""/utf8>>, parsing_escaped_field} -> {error, {unclosed_escaped_field, Field_start}}; {<<"\n"/utf8, Rest@2/binary>>, parsing_unescaped_field} -> Field@3 = extract_field(Original, Field_start, Field_length, Status), Row@5 = lists:reverse([Field@3 | Row]), Rows@1 = [Row@5 | Rows], Field_start@3 = (Field_start + Field_length) + 1, do_parse( Rest@2, Original, Field_start@3, 0, [], Rows@1, newline_found ); {<<"\r\n"/utf8, Rest@3/binary>>, parsing_unescaped_field} -> Field@4 = extract_field(Original, Field_start, Field_length, Status), Row@6 = lists:reverse([Field@4 | Row]), Rows@2 = [Row@6 | Rows], Field_start@4 = (Field_start + Field_length) + 2, do_parse( Rest@3, Original, Field_start@4, 0, [], Rows@2, newline_found ); {<<"\"\n"/utf8, Rest@3/binary>>, parsing_escaped_field} -> Field@4 = extract_field(Original, Field_start, Field_length, Status), Row@6 = lists:reverse([Field@4 | Row]), Rows@2 = [Row@6 | Rows], Field_start@4 = (Field_start + Field_length) + 2, do_parse( Rest@3, Original, Field_start@4, 0, [], Rows@2, newline_found ); {<<"\"\r\n"/utf8, Rest@4/binary>>, parsing_escaped_field} -> Field@5 = extract_field(Original, Field_start, Field_length, Status), Row@7 = lists:reverse([Field@5 | Row]), Rows@3 = [Row@7 | Rows], Field_start@5 = (Field_start + Field_length) + 3, do_parse( Rest@4, Original, Field_start@5, 0, [], Rows@3, newline_found ); {<<"\n"/utf8, Rest@5/binary>>, comma_found} -> Row@8 = lists:reverse([<<""/utf8>> | Row]), Rows@4 = [Row@8 | Rows], do_parse( Rest@5, Original, Field_start + 1, 0, [], Rows@4, newline_found ); {<<"\r\n"/utf8, Rest@6/binary>>, comma_found} -> Row@9 = lists:reverse([<<""/utf8>> | Row]), Rows@5 = [Row@9 | Rows], do_parse( Rest@6, Original, Field_start + 2, 0, [], Rows@5, newline_found ); {<<"\n"/utf8, Rest@7/binary>>, newline_found} -> do_parse(Rest@7, Original, Field_start + 1, 0, Row, Rows, Status); {<<"\r\n"/utf8, Rest@8/binary>>, newline_found} -> do_parse(Rest@8, Original, Field_start + 2, 0, Row, Rows, Status); {<<"\"\""/utf8, Rest@9/binary>>, parsing_escaped_field} -> do_parse( Rest@9, Original, Field_start, Field_length + 2, Row, Rows, Status ); {<<"\""/utf8, _/binary>>, parsing_unescaped_field} -> {error, {unescaped_quote, Field_start + Field_length}}; {<<"\""/utf8, _/binary>>, parsing_escaped_field} -> {error, {unescaped_quote, Field_start + Field_length}}; {<<"\""/utf8, Rest@10/binary>>, comma_found} -> Status@1 = parsing_escaped_field, do_parse(Rest@10, Original, Field_start + 1, 0, Row, Rows, Status@1); {<<"\""/utf8, Rest@10/binary>>, newline_found} -> Status@1 = parsing_escaped_field, do_parse(Rest@10, Original, Field_start + 1, 0, Row, Rows, Status@1); {_, comma_found} -> Status@2 = case Status of parsing_escaped_field -> parsing_escaped_field; comma_found -> parsing_unescaped_field; newline_found -> parsing_unescaped_field; parsing_unescaped_field -> parsing_unescaped_field end, Rest@11 = gsv_ffi:drop_bytes(String, 1), do_parse( Rest@11, Original, Field_start, Field_length + 1, Row, Rows, Status@2 ); {_, newline_found} -> Status@2 = case Status of parsing_escaped_field -> parsing_escaped_field; comma_found -> parsing_unescaped_field; newline_found -> parsing_unescaped_field; parsing_unescaped_field -> parsing_unescaped_field end, Rest@11 = gsv_ffi:drop_bytes(String, 1), do_parse( Rest@11, Original, Field_start, Field_length + 1, Row, Rows, Status@2 ); {_, parsing_unescaped_field} -> Status@2 = case Status of parsing_escaped_field -> parsing_escaped_field; comma_found -> parsing_unescaped_field; newline_found -> parsing_unescaped_field; parsing_unescaped_field -> parsing_unescaped_field end, Rest@11 = gsv_ffi:drop_bytes(String, 1), do_parse( Rest@11, Original, Field_start, Field_length + 1, Row, Rows, Status@2 ); {_, parsing_escaped_field} -> Status@2 = case Status of parsing_escaped_field -> parsing_escaped_field; comma_found -> parsing_unescaped_field; newline_found -> parsing_unescaped_field; parsing_unescaped_field -> parsing_unescaped_field end, Rest@11 = gsv_ffi:drop_bytes(String, 1), do_parse( Rest@11, Original, Field_start, Field_length + 1, Row, Rows, Status@2 ) end. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 79). -spec to_lists(binary()) -> {ok, list(list(binary()))} | {error, parse_error()}. to_lists(Input) -> case Input of <<"\n"/utf8, Rest/binary>> -> to_lists(Rest); <<"\r\n"/utf8, Rest/binary>> -> to_lists(Rest); <<"\""/utf8, Rest@1/binary>> -> do_parse(Rest@1, Input, 1, 0, [], [], parsing_escaped_field); <<","/utf8, Rest@2/binary>> -> do_parse(Rest@2, Input, 1, 0, [<<""/utf8>>], [], comma_found); _ -> do_parse(Input, Input, 0, 0, [], [], parsing_unescaped_field) end. -file("/Users/benjamin/Personal/gsv/src/gsv.gleam", 350). -spec to_dicts(binary()) -> {ok, list(gleam@dict:dict(binary(), binary()))} | {error, parse_error()}. to_dicts(Input) -> gleam@result:map(to_lists(Input), fun(Rows) -> case Rows of [] -> []; [Headers | Rows@1] -> Headers@1 = erlang:list_to_tuple(Headers), gleam@list:map( Rows@1, fun(Row) -> gleam@list:index_fold( Row, maps:new(), fun(Row@1, Field, Index) -> case Field of <<""/utf8>> -> Row@1; _ -> case glearray:get(Headers@1, Index) of {ok, Header} -> gleam@dict:insert( Row@1, Header, Field ); {error, _} -> Row@1 end end end ) end ) end end).