-module(mesv@format).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src\\mesv\\format.gleam").
-export([build/1, set_row_sep/2, set_col_sep/2, set_headers/2, set_escaper/2, set_escape_all/2, format/2]).
-export_type([formatter/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" The module containing the functions for building the `Formatter`, and for using a\n"
" `Formatter(a)` to transform `List(a)` into a `String`, which can be directly written to file.\n"
" \n"
" ## Examples\n"
" A basic example of formatting data\n"
" ```gleam\n"
" import gleam/int\n"
" import mesv/format\n"
" \n"
" const data: List(#(String, Int, Bool)) = [\n"
" #(\"Adam\", 20, True),\n"
" #(\"Beatrice\", 25, True),\n"
" #(\"Colin\", 2, False),\n"
" ]\n"
" \n"
" pub fn main() -> Nil {\n"
" let formatter =\n"
" // First create a formatter\n"
" format.build(fn(val: #(String, Int, Bool)) -> List(String) {\n"
" let #(name, age, adult) = val\n"
" [\n"
" name,\n"
" int.to_string(age),\n"
" case adult {\n"
" True -> \"true\"\n"
" False -> \"false\"\n"
" },\n"
" ]\n"
" })\n"
" \n"
" // Then, use that formatter on the data you want to format\n"
" let formatted_data = format.format(formatter, data)\n"
" \n"
" // By default, the formatter uses the comma as a column separator,\n"
" // newline as the row separator, and doublequotes for escaping cells\n"
" assert formatted_data == \"Adam,20,true\\nBeatrice,25,true\\nColin,2,false\"\n"
" }\n"
" ```\n"
" A cool party trick to impress your friends - computing data *just in time*\n"
" when converting to string, minimizing the memory required!\n"
" ```gleam\n"
" // [...]\n"
" const data: List(#(String, Int)) = [\n"
" #(\"Alex\", 20),\n"
" #(\"Betty\", 25),\n"
" #(\"Conrad\", 2),\n"
" ]\n"
" \n"
" pub fn main() -> Nil {\n"
" let formatted_data =\n"
" format.build(fn(val: #(String, Int)) -> List(String) {\n"
" let #(name, age) = val\n"
" [\n"
" name,\n"
" int.to_string(age),\n"
" bool.to_string(age >= 18),\n"
" ]\n"
" })\n"
" |> format.format(data)\n"
" \n"
" assert formatted_data == \"Alex,20,True\\nBetty,25,True\\nConrad,2,False\"\n"
" }\n"
" ```\n"
" \n"
).
-opaque formatter(DKW) :: {formatter,
binary(),
binary(),
binary(),
boolean(),
gleam@option:option(list(binary())),
fun((DKW) -> list(binary()))}.
-file("src\\mesv\\format.gleam", 93).
?DOC(
" Function for directly building a `Formatter` that outputs the specified\n"
" elements in an exact order.\n"
).
-spec build(fun((DKX) -> list(binary()))) -> formatter(DKX).
build(F) ->
{formatter, <<","/utf8>>, <<"\n"/utf8>>, <<"\""/utf8>>, false, none, F}.
-file("src\\mesv\\format.gleam", 109).
?DOC(
" Function to set a specific row separator, instead of the default newline (`\\n`)\n"
" \n"
" If the row separator chosen is longer than a single character, it might cause problems\n"
" with performance later during parsing.\n"
).
-spec set_row_sep(formatter(DLA), binary()) -> formatter(DLA).
set_row_sep(Formatter, New_row_separator) ->
{formatter,
erlang:element(2, Formatter),
New_row_separator,
erlang:element(4, Formatter),
erlang:element(5, Formatter),
erlang:element(6, Formatter),
erlang:element(7, Formatter)}.
-file("src\\mesv\\format.gleam", 121).
?DOC(
" Function to set a specific column separator, instead of the default comma (`,`)\n"
" \n"
" If the column separator chosen is longer than a single character, it might cause problems\n"
" with performance later during parsing.\n"
).
-spec set_col_sep(formatter(DLD), binary()) -> formatter(DLD).
set_col_sep(Formatter, New_column_separator) ->
{formatter,
New_column_separator,
erlang:element(3, Formatter),
erlang:element(4, Formatter),
erlang:element(5, Formatter),
erlang:element(6, Formatter),
erlang:element(7, Formatter)}.
-file("src\\mesv\\format.gleam", 133).
?DOC(
" Function to manually set column headers in a particular order.\n"
" \n"
" By default, no headers will be written to output String, and the first row will\n"
" directly be the formatted data.\n"
).
-spec set_headers(formatter(DLG), list(binary())) -> formatter(DLG).
set_headers(Formatter, New_headers) ->
{formatter,
erlang:element(2, Formatter),
erlang:element(3, Formatter),
erlang:element(4, Formatter),
erlang:element(5, Formatter),
{some, New_headers},
erlang:element(7, Formatter)}.
-file("src\\mesv\\format.gleam", 143).
?DOC(
" Function to set custom escaper (character that wraps the value if its'\n"
" string contains row or column separators, or the escaper itself)\n"
).
-spec set_escaper(formatter(DLK), binary()) -> formatter(DLK).
set_escaper(Formatter, New_escaper) ->
{formatter,
erlang:element(2, Formatter),
erlang:element(3, Formatter),
New_escaper,
erlang:element(5, Formatter),
erlang:element(6, Formatter),
erlang:element(7, Formatter)}.
-file("src\\mesv\\format.gleam", 154).
?DOC(
" Function to specify whether to wrap each value in an escaper, regardles of necessity.\n"
" \n"
" By default false.\n"
).
-spec set_escape_all(formatter(DLN), boolean()) -> formatter(DLN).
set_escape_all(Parser, Escape_all) ->
{formatter,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
Escape_all,
erlang:element(6, Parser),
erlang:element(7, Parser)}.
-file("src\\mesv\\format.gleam", 165).
?DOC(
" Internal helper function for creating a function that checks if a specific element needs\n"
" to be escaped (wrapped in escaper, which by default is `\"`) before being written to file.\n"
" \n"
" It's a curried function because I like functional programming, and because it *should*\n"
" give some performance improvements if I create such a function before any looping instead\n"
" of constructing one for each iteration.\n"
).
-spec needs_escaping(list(binary())) -> fun((binary()) -> boolean()).
needs_escaping(Prohibited) ->
fun(El) -> _pipe = Prohibited,
gleam@list:any(_pipe, fun(S) -> gleam_stdlib:contains_string(El, S) end) end.
-file("src\\mesv\\format.gleam", 178).
?DOC(
" Internal helper function for creating a function for 'escaping' an element (for each `rule`,\n"
" replacing the first element in the tuple with the second).\n"
" \n"
" It's a curried function because I like functional programming, and because it *should*\n"
" give some performance improvements if I create such a function before any looping instead\n"
" of constructing one for each iteration.\n"
).
-spec escape(list({binary(), binary()})) -> fun((binary()) -> binary()).
escape(Rules) ->
fun(El) -> _pipe = Rules,
_pipe@1 = gleam@list:map(
_pipe,
fun(Rule) ->
fun(_capture) ->
gleam@string:replace(
_capture,
erlang:element(1, Rule),
erlang:element(2, Rule)
)
end
end
),
gleam@list:fold(_pipe@1, El, fun(Acc, Rule@1) -> Rule@1(Acc) end) end.
-file("src\\mesv\\format.gleam", 197).
?DOC(
" Internal helper function for creating a function that wraps a String in the specified\n"
" 'escaper' String.\n"
" \n"
" It's a curried function because I like functional programming, and because it *should*\n"
" give some performance improvements if I create such a function before any looping instead\n"
" of constructing one for each iteration.\n"
).
-spec wrap(binary()) -> fun((binary()) -> binary()).
wrap(In) ->
fun(El) -> <<<<In/binary, El/binary>>/binary, In/binary>> end.
-file("src\\mesv\\format.gleam", 207).
?DOC(
" Execution function that takes in a `Formatter(a)` as well as a `List(a)`,\n"
" and encodes it into a String.\n"
" \n"
" All of the configuration options need to be set when building the `Formatter`,\n"
" so this function is very simple to understand.\n"
).
-spec format(formatter(DLS), list(DLS)) -> binary().
format(Formatter, Elements) ->
{formatter,
Column_separator,
Row_separator,
Escaper,
Escape_all,
Maybe_headers,
To_string} = Formatter,
Rules = [{Escaper, <<Escaper/binary, Escaper/binary>>}],
Escapeify = fun(El) -> _pipe = El,
_pipe@1 = gleam@string:trim(_pipe),
_pipe@2 = (escape(Rules))(_pipe@1),
(wrap(Escaper))(_pipe@2) end,
To_escape = needs_escaping(
[Column_separator, Row_separator, Escaper, <<"\n"/utf8>>, <<"\r"/utf8>>]
),
Ensafeify = fun(Val) -> case Escape_all orelse To_escape(Val) of
true ->
Escapeify(Val);
false ->
Val
end end,
_pipe@5 = case Maybe_headers of
{some, Headers} ->
[Headers |
begin
_pipe@3 = Elements,
gleam@list:map(_pipe@3, To_string)
end];
none ->
_pipe@4 = Elements,
gleam@list:map(_pipe@4, To_string)
end,
_pipe@9 = gleam@list:map(_pipe@5, fun(Values) -> _pipe@6 = Values,
_pipe@7 = gleam@list:map(_pipe@6, fun gleam@string:trim/1),
_pipe@8 = gleam@list:map(_pipe@7, Ensafeify),
gleam@string:join(_pipe@8, Column_separator) end),
gleam@string:join(_pipe@9, Row_separator).