Current section

Files

Jump to
squirrel src squirrel.erl
Raw

src/squirrel.erl

-module(squirrel).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([exit/1, main/0]).
-spec check_scheme(gleam@option:option(binary())) -> {ok, nil} | {error, nil}.
check_scheme(Scheme) ->
case Scheme of
{some, <<"postgres"/utf8>>} ->
{ok, nil};
{some, <<"postgresql"/utf8>>} ->
{ok, nil};
none ->
{ok, nil};
{some, _} ->
{error, nil}
end.
-spec parse_user_and_password_from_userinfo(gleam@option:option(binary())) -> {gleam@option:option(binary()),
gleam@option:option(binary())}.
parse_user_and_password_from_userinfo(Userinfo) ->
case Userinfo of
none ->
{none, none};
{some, Userinfo@1} ->
case gleam@string:split(Userinfo@1, <<":"/utf8>>) of
[User] ->
{{some, User}, none};
[User@1, Password | _] ->
{{some, User@1}, {some, Password}};
_ ->
{none, none}
end
end.
-spec parse_database_from_path(binary()) -> gleam@option:option(binary()).
parse_database_from_path(Path) ->
case gleam@string:split(Path, <<"/"/utf8>>) of
[<<""/utf8>>, Database | _] ->
{some, Database};
_ ->
none
end.
-spec walk(binary()) -> gleam@dict:dict(binary(), list(binary())).
walk(From) ->
case filepath:base_name(From) of
<<"sql"/utf8>> ->
_assert_subject = simplifile_erl:read_directory(From),
{ok, Files} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"squirrel"/utf8>>,
function => <<"walk"/utf8>>,
line => 193})
end,
Files@1 = (gleam@list:filter_map(
Files,
fun(File) ->
gleam@result:'try'(
filepath:extension(File),
fun(Extension) ->
gleam@bool:guard(
Extension /= <<"sql"/utf8>>,
{error, nil},
fun() ->
File_name = filepath:join(From, File),
case simplifile_erl:is_file(File_name) of
{ok, true} ->
{ok, File_name};
{ok, false} ->
{error, nil};
{error, _} ->
{error, nil}
end
end
)
end
)
end
)),
maps:from_list([{From, Files@1}]);
_ ->
_assert_subject@1 = simplifile_erl:read_directory(From),
{ok, Files@2} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"squirrel"/utf8>>,
function => <<"walk"/utf8>>,
line => 208})
end,
Directories = (gleam@list:filter_map(
Files@2,
fun(File@1) ->
File_name@1 = filepath:join(From, File@1),
case simplifile_erl:is_directory(File_name@1) of
{ok, true} ->
{ok, File_name@1};
{ok, false} ->
{error, nil};
{error, _} ->
{error, nil}
end
end
)),
_pipe = gleam@list:map(Directories, fun walk/1),
gleam@list:fold(_pipe, gleam@dict:new(), fun gleam@dict:merge/2)
end.
-spec term_width() -> integer().
term_width() ->
_pipe = term_size:columns(),
gleam@result:unwrap(_pipe, 80).
-spec pluralise(integer(), binary(), binary()) -> binary().
pluralise(Count, Singular, Plural) ->
case Count of
1 ->
Singular;
_ ->
Plural
end.
-spec flexible_string(binary()) -> glam@doc:document().
flexible_string(String) ->
_pipe = gleam@string:split(String, <<"\n"/utf8>>),
_pipe@1 = gleam@list:flat_map(
_pipe,
fun(_capture) -> gleam@string:split(_capture, <<" "/utf8>>) end
),
_pipe@2 = gleam@list:map(_pipe@1, fun glam@doc:from_string/1),
_pipe@3 = glam@doc:join(_pipe@2, {flex_break, <<" "/utf8>>, <<""/utf8>>}),
glam@doc:group(_pipe@3).
-spec text_with_header(binary(), binary()) -> glam@doc:document().
text_with_header(Header, Text) ->
_pipe@1 = [glam@doc:from_string(Header),
begin
_pipe = flexible_string(Text),
glam@doc:nest(_pipe, gleam@string:length(Header))
end],
_pipe@2 = glam@doc:concat(_pipe@1),
glam@doc:group(_pipe@2).
-spec pretty_report(
gleam@dict:dict(binary(), {integer(), list(squirrel@internal@error:error())})
) -> {binary(), integer()}.
pretty_report(Dirs) ->
{Ok@1, Errors@1} = (gleam@dict:fold(
Dirs,
{0, []},
fun(_use0, _, Result) ->
{All_ok, All_errors} = _use0,
{Ok, Errors} = Result,
{All_ok + Ok,
begin
_pipe = Errors,
lists:append(_pipe, All_errors)
end}
end
)),
Errors_doc = begin
_pipe@1 = gleam@list:map(Errors@1, fun squirrel@internal@error:to_doc/1),
glam@doc:join(_pipe@1, glam@doc:lines(2))
end,
Status_code = case Errors@1 of
[_ | _] ->
1;
[] ->
0
end,
Report = case {Ok@1, Errors@1} of
{0, [_ | _]} ->
glam@doc:to_string(Errors_doc, term_width());
{0, []} ->
_pipe@2 = [text_with_header(
<<"🐿️ "/utf8>>,
<<"I couldn't find any `*.sql` file to generate queries from."/utf8>>
),
glam@doc:lines(2),
flexible_string(
<<"Hint: I look for all `*.sql` files in any directory called `sql`
under your project's `src` directory."/utf8>>
)],
_pipe@3 = glam@doc:concat(_pipe@2),
_pipe@4 = glam@doc:to_string(_pipe@3, term_width()),
gleam_community@ansi:yellow(_pipe@4);
{N, []} ->
_pipe@5 = text_with_header(
<<"🐿️ "/utf8>>,
<<<<<<<<"Generated "/utf8, (gleam@int:to_string(N))/binary>>/binary,
" "/utf8>>/binary,
(pluralise(N, <<"query"/utf8>>, <<"queries"/utf8>>))/binary>>/binary,
"!"/utf8>>
),
_pipe@6 = glam@doc:to_string(_pipe@5, term_width()),
gleam_community@ansi:green(_pipe@6);
{N@1, [_ | _]} ->
_pipe@7 = [Errors_doc,
glam@doc:lines(2),
text_with_header(
<<"🥜 "/utf8>>,
<<<<<<<<"I could still generate "/utf8,
(gleam@int:to_string(N@1))/binary>>/binary,
" "/utf8>>/binary,
(pluralise(
N@1,
<<"query"/utf8>>,
<<"queries"/utf8>>
))/binary>>/binary,
"."/utf8>>
)],
_pipe@8 = glam@doc:concat(_pipe@7),
glam@doc:to_string(_pipe@8, term_width())
end,
{Report, Status_code}.
-spec exit(integer()) -> nil.
exit(N) ->
squirrel_ffi:exit(N).
-spec write_queries(list(squirrel@internal@query:typed_query()), binary()) -> {ok,
integer()} |
{error, squirrel@internal@error:error()}.
write_queries(Queries, File) ->
gleam@bool:guard(
Queries =:= [],
{ok, 0},
fun() ->
Directory = filepath:directory_name(File),
_ = simplifile:create_directory_all(Directory),
Code = squirrel@internal@query:generate_code(
Queries,
<<"v1.8.1"/utf8>>
),
Try_write = begin
_pipe = simplifile:write(File, Code),
gleam@result:map_error(
_pipe,
fun(_capture) -> {cannot_write_to_file, File, _capture} end
)
end,
gleam@result:'try'(
Try_write,
fun(_) -> {ok, erlang:length(Queries)} end
)
end
).
-spec run(
gleam@dict:dict(binary(), list(binary())),
squirrel@internal@database@postgres:connection_options()
) -> gleam@dict:dict(binary(), {integer(),
list(squirrel@internal@error:error())}).
run(Directories, Connection) ->
gleam@dict:map_values(
Directories,
fun(Directory, Files) ->
{Queries, Errors} = begin
_pipe = gleam@list:map(
Files,
fun squirrel@internal@query:from_file/1
),
gleam@result:partition(_pipe)
end,
{Queries@2, Errors@1} = case squirrel@internal@database@postgres:main(
Queries,
Connection
) of
{error, Error} ->
{[], [Error | Errors]};
{ok, {Queries@1, Type_errors}} ->
{Queries@1, lists:append(Errors, Type_errors)}
end,
Output_file = begin
_pipe@1 = filepath:directory_name(Directory),
filepath:join(_pipe@1, <<"sql.gleam"/utf8>>)
end,
case write_queries(Queries@2, Output_file) of
{ok, N} ->
{N, Errors@1};
{error, Error@1} ->
{erlang:length(Queries@2), [Error@1 | Errors@1]}
end
end
).
-spec connection_options_from_variables() -> squirrel@internal@database@postgres:connection_options().
connection_options_from_variables() ->
Host = begin
_pipe = envoy_ffi:get(<<"PGHOST"/utf8>>),
gleam@result:unwrap(_pipe, <<"localhost"/utf8>>)
end,
User = begin
_pipe@1 = envoy_ffi:get(<<"PGUSER"/utf8>>),
gleam@result:unwrap(_pipe@1, <<"postgres"/utf8>>)
end,
Password = begin
_pipe@2 = envoy_ffi:get(<<"PGPASSWORD"/utf8>>),
gleam@result:unwrap(_pipe@2, <<""/utf8>>)
end,
Database = begin
_pipe@3 = envoy_ffi:get(<<"PGDATABASE"/utf8>>),
_pipe@4 = gleam@result:'or'(_pipe@3, squirrel@internal@project:name()),
gleam@result:unwrap(_pipe@4, <<"database"/utf8>>)
end,
Port = begin
_pipe@5 = envoy_ffi:get(<<"PGPORT"/utf8>>),
_pipe@6 = gleam@result:then(_pipe@5, fun gleam@int:parse/1),
gleam@result:unwrap(_pipe@6, 5432)
end,
{connection_options, Host, Port, User, Password, Database, 1000}.
-spec parse_connection_url(binary()) -> {ok,
squirrel@internal@database@postgres:connection_options()} |
{error, nil}.
parse_connection_url(Raw) ->
gleam@result:'try'(
gleam@uri:parse(Raw),
fun(Uri) ->
{uri, Scheme, Userinfo, Host, Port, Path, _, _} = Uri,
gleam@result:'try'(
check_scheme(Scheme),
fun(_) ->
{User, Password} = parse_user_and_password_from_userinfo(
Userinfo
),
Database = parse_database_from_path(Path),
{ok,
{connection_options,
begin
_pipe = Host,
gleam@option:unwrap(_pipe, <<"localhost"/utf8>>)
end,
begin
_pipe@1 = Port,
gleam@option:unwrap(_pipe@1, 5432)
end,
begin
_pipe@2 = User,
gleam@option:unwrap(
_pipe@2,
<<"postgres"/utf8>>
)
end,
begin
_pipe@3 = Password,
gleam@option:unwrap(_pipe@3, <<""/utf8>>)
end,
begin
_pipe@4 = Database,
gleam@option:unwrap(
_pipe@4,
<<"database"/utf8>>
)
end,
1000}}
end
)
end
).
-spec connection_options() -> {ok,
squirrel@internal@database@postgres:connection_options()} |
{error, squirrel@internal@error:error()}.
connection_options() ->
case envoy_ffi:get(<<"DATABASE_URL"/utf8>>) of
{ok, Url} ->
_pipe = parse_connection_url(Url),
gleam@result:replace_error(_pipe, {invalid_connection_string, Url});
{error, _} ->
{ok, connection_options_from_variables()}
end.
-spec main() -> nil.
main() ->
case connection_options() of
{error, Error} ->
_pipe = squirrel@internal@error:to_doc(Error),
_pipe@1 = glam@doc:to_string(_pipe, term_width()),
gleam@io:println(_pipe@1),
squirrel_ffi:exit(1);
{ok, Options} ->
{Report, Status_code} = begin
_pipe@2 = walk(squirrel@internal@project:src()),
_pipe@3 = run(_pipe@2, Options),
pretty_report(_pipe@3)
end,
gleam@io:println(Report),
squirrel_ffi:exit(Status_code)
end.