Current section
Files
Jump to
Current section
Files
src/cigogne@internal@parser_formatter.erl
-module(cigogne@internal@parser_formatter).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/cigogne/internal/parser_formatter.gleam").
-export([parse/1, format/1, get_error_message/1]).
-export_type([parser_error/0, query_context/0]).
-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(false).
-type parser_error() :: {missing_up_guard, binary()} |
{missing_down_guard, binary()} |
{missing_end_guard, binary()} |
{empty_up_migration, binary()} |
{empty_down_migration, binary()} |
{unfinished_literal, binary()} |
{wrong_format, binary()} |
{migration_error, cigogne@migration:migration_error()} |
{not_a_s_q_l_file, binary()} |
empty_path.
-type query_context() :: simple_context |
in_string_literal |
{in_dollar_quoted, binary()} |
{in_dollar_tag, list(binary()), gleam@option:option(binary())}.
-file("src/cigogne/internal/parser_formatter.gleam", 79).
?DOC(false).
-spec parse_timestamp(binary()) -> {ok, gleam@time@timestamp:timestamp()} |
{error, nil}.
parse_timestamp(Timestamp_str) ->
gleam@bool:guard(
string:length(Timestamp_str) /= 14,
{error, nil},
fun() ->
Year = begin
_pipe = gleam@string:slice(Timestamp_str, 0, 4),
gleam_stdlib:parse_int(_pipe)
end,
Month = begin
_pipe@1 = gleam@string:slice(Timestamp_str, 4, 2),
gleam_stdlib:parse_int(_pipe@1)
end,
Day = begin
_pipe@2 = gleam@string:slice(Timestamp_str, 6, 2),
gleam_stdlib:parse_int(_pipe@2)
end,
Hours = begin
_pipe@3 = gleam@string:slice(Timestamp_str, 8, 2),
gleam_stdlib:parse_int(_pipe@3)
end,
Minutes = begin
_pipe@4 = gleam@string:slice(Timestamp_str, 10, 2),
gleam_stdlib:parse_int(_pipe@4)
end,
Seconds = begin
_pipe@5 = gleam@string:slice(Timestamp_str, 12, 2),
gleam_stdlib:parse_int(_pipe@5)
end,
case {Year, Month, Day, Hours, Minutes, Seconds} of
{{ok, Y}, {ok, M}, {ok, D}, {ok, H}, {ok, Min}, {ok, S}} ->
_pipe@6 = gleam@time@calendar:month_from_int(M),
gleam@result:map(
_pipe@6,
fun(M@1) ->
gleam@time@timestamp:from_calendar(
{date, Y, M@1, D},
{time_of_day, H, Min, S, 0},
{duration, 0, 0}
)
end
);
{_, _, _, _, _, _} ->
{error, nil}
end
end
).
-file("src/cigogne/internal/parser_formatter.gleam", 60).
?DOC(false).
-spec parse_fullname(binary()) -> {ok,
{gleam@time@timestamp:timestamp(), binary()}} |
{error, parser_error()}.
parse_fullname(Fullname) ->
gleam@result:'try'(
begin
_pipe = gleam@string:split_once(Fullname, <<"-"/utf8>>),
gleam@result:replace_error(_pipe, {wrong_format, Fullname})
end,
fun(_use0) ->
{Timestamp_str, Name_str} = _use0,
gleam@result:'try'(
begin
_pipe@1 = parse_timestamp(Timestamp_str),
gleam@result:replace_error(
_pipe@1,
{wrong_format, Fullname}
)
end,
fun(Timestamp) ->
gleam@result:'try'(
begin
_pipe@2 = cigogne@migration:check_name(Name_str),
gleam@result:map_error(
_pipe@2,
fun(Field@0) -> {migration_error, Field@0} end
)
end,
fun(Name) -> {ok, {Timestamp, Name}} end
)
end
)
end
).
-file("src/cigogne/internal/parser_formatter.gleam", 245).
?DOC(false).
-spec add_if_not_empty(list(binary()), list(binary())) -> list(binary()).
add_if_not_empty(String_list, Graphemes_to_add) ->
case Graphemes_to_add of
[] ->
String_list;
_ ->
[begin
_pipe = Graphemes_to_add,
_pipe@1 = lists:reverse(_pipe),
gleam@string:join(_pipe@1, <<""/utf8>>)
end |
String_list]
end.
-file("src/cigogne/internal/parser_formatter.gleam", 155).
?DOC(false).
-spec analyse_sql_graphemes(
list(binary()),
list(binary()),
query_context(),
list(binary())
) -> {ok, list(binary())} | {error, parser_error()}.
analyse_sql_graphemes(Sql_graphemes, Previous_graphemes, Context, Queries) ->
case {Sql_graphemes, Context} of
{[], simple_context} ->
_pipe = Queries,
_pipe@1 = add_if_not_empty(_pipe, Previous_graphemes),
{ok, _pipe@1};
{[], in_string_literal} ->
{error, {unfinished_literal, <<"string literal"/utf8>>}};
{[], {in_dollar_quoted, Tag}} ->
{error,
{unfinished_literal,
<<<<"dollar quoted text with tag '"/utf8, Tag/binary>>/binary,
"'"/utf8>>}};
{[], {in_dollar_tag, _, _}} ->
{error, {unfinished_literal, <<"dollar tag"/utf8>>}};
{[<<";"/utf8>> | Rest], simple_context} ->
analyse_sql_graphemes(
Rest,
[],
simple_context,
begin
_pipe@2 = Queries,
add_if_not_empty(_pipe@2, Previous_graphemes)
end
);
{[<<"'"/utf8>> | Rest@1], simple_context} ->
analyse_sql_graphemes(
Rest@1,
[<<"'"/utf8>> | Previous_graphemes],
in_string_literal,
Queries
);
{[<<"'"/utf8>> | Rest@2], in_string_literal} ->
analyse_sql_graphemes(
Rest@2,
[<<"'"/utf8>> | Previous_graphemes],
simple_context,
Queries
);
{[<<"$"/utf8>> | Rest@3], simple_context} ->
analyse_sql_graphemes(
Rest@3,
[<<"$"/utf8>> | Previous_graphemes],
{in_dollar_tag, [], none},
Queries
);
{[<<"$"/utf8>> | Rest@4], {in_dollar_tag, Tag@1, none}} ->
analyse_sql_graphemes(
Rest@4,
[<<"$"/utf8>> | Previous_graphemes],
{in_dollar_quoted,
begin
_pipe@3 = Tag@1,
_pipe@4 = lists:reverse(_pipe@3),
gleam@string:join(_pipe@4, <<""/utf8>>)
end},
Queries
);
{[<<"$"/utf8>> | Rest@5], {in_dollar_tag, Tag_graphemes, {some, Tag@2}}} ->
analyse_sql_graphemes(
Rest@5,
[<<"$"/utf8>> | Previous_graphemes],
case begin
_pipe@5 = Tag_graphemes,
_pipe@6 = lists:reverse(_pipe@5),
gleam@string:join(_pipe@6, <<""/utf8>>)
end of
A when A =:= Tag@2 ->
simple_context;
_ ->
{in_dollar_quoted, Tag@2}
end,
Queries
);
{[<<"$"/utf8>> | Rest@6], {in_dollar_quoted, Tag@3}} ->
analyse_sql_graphemes(
Rest@6,
[<<"$"/utf8>> | Previous_graphemes],
{in_dollar_tag, [], {some, Tag@3}},
Queries
);
{[G | Rest@7], {in_dollar_tag, [], Parent}} ->
analyse_sql_graphemes(Rest@7, [G | Previous_graphemes], case G of
<<"0"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"1"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"2"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"3"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"4"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"5"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"6"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"7"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"8"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
<<"9"/utf8>> ->
case Parent of
none ->
simple_context;
{some, Tag@4} ->
{in_dollar_quoted, Tag@4}
end;
_ ->
{in_dollar_tag, [G], Parent}
end, Queries);
{[G@1 | Rest@8], {in_dollar_tag, Tag_graphemes@1, Parent@1}} ->
analyse_sql_graphemes(
Rest@8,
[G@1 | Previous_graphemes],
{in_dollar_tag, [G@1 | Tag_graphemes@1], Parent@1},
Queries
);
{[G@2 | Rest@9], Ctx} ->
analyse_sql_graphemes(
Rest@9,
[G@2 | Previous_graphemes],
Ctx,
Queries
)
end.
-file("src/cigogne/internal/parser_formatter.gleam", 133).
?DOC(false).
-spec split_queries(binary()) -> {ok, list(binary())} | {error, parser_error()}.
split_queries(Queries) ->
Graphemes = begin
_pipe = Queries,
gleam@string:to_graphemes(_pipe)
end,
gleam@result:'try'(
analyse_sql_graphemes(Graphemes, [], simple_context, []),
fun(Queries@1) -> _pipe@1 = Queries@1,
_pipe@2 = lists:reverse(_pipe@1),
_pipe@3 = gleam@list:map(_pipe@2, fun gleam@string:trim/1),
_pipe@4 = gleam@list:filter(
_pipe@3,
fun(Q) -> not gleam@string:is_empty(Q) end
),
_pipe@5 = gleam@list:map(
_pipe@4,
fun(Q@1) -> <<Q@1/binary, ";"/utf8>> end
),
{ok, _pipe@5} end
).
-file("src/cigogne/internal/parser_formatter.gleam", 104).
?DOC(false).
-spec parse_content(cigogne@internal@fs:file()) -> {ok,
{list(binary()), list(binary())}} |
{error, parser_error()}.
parse_content(File) ->
gleam@result:'try'(
begin
_pipe = erlang:element(3, File),
_pipe@1 = gleam@string:split_once(
_pipe,
<<"--- migration:up"/utf8>>
),
gleam@result:replace_error(
_pipe@1,
{missing_up_guard, erlang:element(2, File)}
)
end,
fun(_use0) ->
{_, Up_and_rest} = _use0,
gleam@result:'try'(
begin
_pipe@2 = Up_and_rest,
_pipe@3 = gleam@string:split_once(
_pipe@2,
<<"--- migration:down"/utf8>>
),
gleam@result:replace_error(
_pipe@3,
{missing_down_guard, erlang:element(2, File)}
)
end,
fun(_use0@1) ->
{Up, Down_and_rest} = _use0@1,
gleam@result:'try'(
begin
_pipe@4 = Down_and_rest,
_pipe@5 = gleam@string:split_once(
_pipe@4,
<<"--- migration:end"/utf8>>
),
gleam@result:replace_error(
_pipe@5,
{missing_end_guard, erlang:element(2, File)}
)
end,
fun(_use0@2) ->
{Down, _} = _use0@2,
gleam@result:'try'(
split_queries(Up),
fun(Queries_up) ->
gleam@result:'try'(
split_queries(Down),
fun(Queries_down) ->
case {Queries_up, Queries_down} of
{[], _} ->
{error,
{empty_up_migration,
erlang:element(
2,
File
)}};
{_, []} ->
{error,
{empty_down_migration,
erlang:element(
2,
File
)}};
{Ups, Downs} ->
{ok, {Ups, Downs}}
end
end
)
end
)
end
)
end
)
end
).
-file("src/cigogne/internal/parser_formatter.gleam", 32).
?DOC(false).
-spec parse(cigogne@internal@fs:file()) -> {ok, cigogne@migration:migration()} |
{error, parser_error()}.
parse(Migration_file) ->
gleam@bool:guard(
not gleam_stdlib:string_ends_with(
erlang:element(2, Migration_file),
<<".sql"/utf8>>
),
{error, {not_a_s_q_l_file, erlang:element(2, Migration_file)}},
fun() ->
gleam@result:'try'(
begin
_pipe = erlang:element(2, Migration_file),
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
_pipe@2 = gleam@list:last(_pipe@1),
_pipe@3 = gleam@result:replace_error(_pipe@2, empty_path),
gleam@result:map(
_pipe@3,
fun(_capture) -> gleam@string:drop_end(_capture, 4) end
)
end,
fun(Filename_without_ext) ->
gleam@result:'try'(
parse_fullname(Filename_without_ext),
fun(_use0) ->
{Timestamp, Name} = _use0,
gleam@result:'try'(
parse_content(Migration_file),
fun(_use0@1) ->
{Ups, Downs} = _use0@1,
{ok,
{migration,
erlang:element(2, Migration_file),
Timestamp,
Name,
Ups,
Downs,
cigogne@internal@utils:make_sha256(
erlang:element(
3,
Migration_file
)
)}}
end
)
end
)
end
)
end
).
-file("src/cigogne/internal/parser_formatter.gleam", 255).
?DOC(false).
-spec format(cigogne@migration:migration()) -> cigogne@internal@fs:file().
format(Migration) ->
Content = <<<<<<<<<<<<<<<<"--- migration:up"/utf8, "\n"/utf8>>/binary,
(begin
_pipe = erlang:element(5, Migration),
gleam@string:join(_pipe, <<"\n"/utf8>>)
end)/binary>>/binary,
"\n"/utf8>>/binary,
"--- migration:down"/utf8>>/binary,
"\n"/utf8>>/binary,
(begin
_pipe@1 = erlang:element(6, Migration),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end)/binary>>/binary,
"\n"/utf8>>/binary,
"--- migration:end"/utf8>>,
{file, erlang:element(2, Migration), Content}.
-file("src/cigogne/internal/parser_formatter.gleam", 270).
?DOC(false).
-spec get_error_message(parser_error()) -> binary().
get_error_message(Error) ->
case Error of
{empty_down_migration, Filepath} ->
<<<<"In file "/utf8, Filepath/binary>>/binary,
": Empty down migration"/utf8>>;
empty_path ->
<<"Cannot parse migration from an empty path"/utf8>>;
{empty_up_migration, Filepath@1} ->
<<<<"In file "/utf8, Filepath@1/binary>>/binary,
": Empty up migration"/utf8>>;
{missing_down_guard, Filepath@2} ->
<<<<<<<<"In file "/utf8, Filepath@2/binary>>/binary,
": Missing down guard ("/utf8>>/binary,
"--- migration:down"/utf8>>/binary,
")"/utf8>>;
{missing_end_guard, Filepath@3} ->
<<<<<<<<"In file "/utf8, Filepath@3/binary>>/binary,
": Missing up guard ("/utf8>>/binary,
"--- migration:up"/utf8>>/binary,
")"/utf8>>;
{missing_up_guard, Filepath@4} ->
<<<<<<<<"In file "/utf8, Filepath@4/binary>>/binary,
": Missing end guard ("/utf8>>/binary,
"--- migration:end"/utf8>>/binary,
")"/utf8>>;
{migration_error, Error@1} ->
<<"Migration error: "/utf8,
(cigogne@migration:get_error_message(Error@1))/binary>>;
{unfinished_literal, Context} ->
<<"Invalid migration: Unfinished "/utf8, Context/binary>>;
{wrong_format, Name} ->
<<Name/binary,
" isn't a valid migration name ! It should be YYYYMMDDHHmmss-<NAME>"/utf8>>;
{not_a_s_q_l_file, Filepath@5} ->
<<Filepath@5/binary, " is not a .sql file"/utf8>>
end.