Current section

Files

Jump to
cigogne src cigogne@internal@parser_formatter.erl
Raw

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 |
{string_literal, query_context()} |
{comment, query_context()} |
{c_style_comment, query_context()} |
{dollar_tag, query_context()} |
{dollar_quoted, binary(), query_context()}.
-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", 155).
?DOC(false).
-spec do_split(
splitter:splitter(),
binary(),
query_context(),
list(binary()),
binary()
) -> {ok, list(binary())} | {error, parser_error()}.
do_split(Splitter, Content, Context, Queries, Current_query) ->
case {splitter_ffi:split(Splitter, Content), Context} of
{{Before, <<";"/utf8>>, After}, simple} ->
do_split(
Splitter,
After,
simple,
[<<Current_query/binary, Before/binary>> | Queries],
<<""/utf8>>
);
{{Before@1, <<"--"/utf8>>, After@1}, simple} ->
do_split(
Splitter,
After@1,
{comment, Context},
Queries,
<<Current_query/binary, Before@1/binary>>
);
{{Before@2, <<"/*"/utf8>>, After@2}, simple} ->
do_split(
Splitter,
After@2,
{c_style_comment, Context},
Queries,
<<Current_query/binary, Before@2/binary>>
);
{{Before@3, <<"'"/utf8>>, After@3}, simple} ->
do_split(
Splitter,
After@3,
{string_literal, Context},
Queries,
<<<<Current_query/binary, Before@3/binary>>/binary, "'"/utf8>>
);
{{Before@4, <<"$"/utf8>>, After@4}, simple} ->
case After@4 of
<<"0"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"1"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"2"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"3"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"4"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"5"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"6"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"7"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"8"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"9"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
_ ->
do_split(
Splitter,
After@4,
{dollar_tag, Context},
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
)
end;
{{Before@4, <<"$"/utf8>>, After@4}, {dollar_quoted, _, _}} ->
case After@4 of
<<"0"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"1"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"2"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"3"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"4"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"5"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"6"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"7"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"8"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
<<"9"/utf8, _/binary>> ->
do_split(
Splitter,
After@4,
Context,
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
);
_ ->
do_split(
Splitter,
After@4,
{dollar_tag, Context},
Queries,
<<<<Current_query/binary, Before@4/binary>>/binary,
"$"/utf8>>
)
end;
{{Before@5, <<"'"/utf8>>, After@5}, {string_literal, Parent_ctx}} ->
do_split(
Splitter,
After@5,
Parent_ctx,
Queries,
<<<<Current_query/binary, Before@5/binary>>/binary, "'"/utf8>>
);
{{_, <<"\n"/utf8>>, After@6}, {comment, Parent_ctx@1}} ->
do_split(Splitter, After@6, Parent_ctx@1, Queries, Current_query);
{{_, <<"*/"/utf8>>, After@7}, {c_style_comment, Parent_ctx@2}} ->
do_split(Splitter, After@7, Parent_ctx@2, Queries, Current_query);
{{Before@6, <<"$"/utf8>>, After@8},
{dollar_tag, {dollar_quoted, Tag, Parent}}} ->
case Before@6 =:= Tag of
true ->
do_split(
Splitter,
After@8,
Parent,
Queries,
<<<<Current_query/binary, Before@6/binary>>/binary,
"$"/utf8>>
);
false ->
do_split(
Splitter,
After@8,
{dollar_quoted, Before@6, {dollar_quoted, Tag, Parent}},
Queries,
<<<<Current_query/binary, Before@6/binary>>/binary,
"$"/utf8>>
)
end;
{{Before@7, <<"$"/utf8>>, After@9}, {dollar_tag, Parent_ctx@3}} ->
do_split(
Splitter,
After@9,
{dollar_quoted, Before@7, Parent_ctx@3},
Queries,
<<<<Current_query/binary, Before@7/binary>>/binary, "$"/utf8>>
);
{{Before@8, <<""/utf8>>, _}, simple} ->
{ok, [<<Current_query/binary, Before@8/binary>> | Queries]};
{{_, <<""/utf8>>, _}, {comment, _}} ->
{ok, Queries};
{{_, <<""/utf8>>, _}, {c_style_comment, _}} ->
{error, {unfinished_literal, <<"C-style comment"/utf8>>}};
{{_, <<""/utf8>>, _}, {string_literal, _}} ->
{error, {unfinished_literal, <<"string literal"/utf8>>}};
{{_, <<""/utf8>>, _}, {dollar_tag, _}} ->
{error, {unfinished_literal, <<"dollar tag"/utf8>>}};
{{_, <<""/utf8>>, _}, {dollar_quoted, Tag@1, _}} ->
{error,
{unfinished_literal,
<<<<"dollar quoted text with tag '"/utf8, Tag@1/binary>>/binary,
"'"/utf8>>}};
{{_, _, After@10}, {comment, _}} ->
do_split(Splitter, After@10, Context, Queries, Current_query);
{{_, _, After@10}, {c_style_comment, _}} ->
do_split(Splitter, After@10, Context, Queries, Current_query);
{{Before@9, Delim, After@11}, Ctx} ->
do_split(
Splitter,
After@11,
Ctx,
Queries,
<<<<Current_query/binary, Before@9/binary>>/binary,
Delim/binary>>
)
end.
-file("src/cigogne/internal/parser_formatter.gleam", 133).
?DOC(false).
-spec split_queries(binary()) -> {ok, list(binary())} | {error, parser_error()}.
split_queries(Queries) ->
Splitter = splitter:new(
[<<"--"/utf8>>,
<<";"/utf8>>,
<<"$"/utf8>>,
<<"'"/utf8>>,
<<"\n"/utf8>>,
<<"/*"/utf8>>,
<<"*/"/utf8>>]
),
gleam@result:'try'(
do_split(Splitter, Queries, simple, [], <<""/utf8>>),
fun(Queries@1) -> _pipe = Queries@1,
_pipe@1 = lists:reverse(_pipe),
_pipe@2 = gleam@list:map(_pipe@1, fun gleam@string:trim/1),
_pipe@3 = gleam@list:filter(
_pipe@2,
fun(Q) -> not gleam@string:is_empty(Q) end
),
_pipe@4 = gleam@list:map(
_pipe@3,
fun(Q@1) -> <<Q@1/binary, ";"/utf8>> end
),
{ok, _pipe@4} 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", 276).
?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", 291).
?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.