Current section
Files
Jump to
Current section
Files
src/squirrel@internal@query.erl
-module(squirrel@internal@query).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([add_types/3, string/1, var/2, list/1, from_file/1, 'fun'/3, record/2, pipe_call/3, decoder/2, generate_code/2]).
-export_type([untyped_query/0, typed_query/0]).
-type untyped_query() :: {untyped_query,
binary(),
integer(),
squirrel@internal@gleam:value_identifier(),
list(binary()),
binary()}.
-type typed_query() :: {typed_query,
binary(),
integer(),
squirrel@internal@gleam:value_identifier(),
list(binary()),
binary(),
list(squirrel@internal@gleam:type()),
list(squirrel@internal@gleam:field())}.
-spec add_types(
untyped_query(),
list(squirrel@internal@gleam:type()),
list(squirrel@internal@gleam:field())
) -> typed_query().
add_types(Query, Params, Returns) ->
{untyped_query, File, Starting_line, Name, Comment, Content} = Query,
{typed_query, File, Starting_line, Name, Comment, Content, Params, Returns}.
-spec gleam_type_to_decoder(squirrel@internal@gleam:type()) -> binary().
gleam_type_to_decoder(Type_) ->
case Type_ of
{list, Type_@1} ->
<<<<"decode.list("/utf8, (gleam_type_to_decoder(Type_@1))/binary>>/binary,
")"/utf8>>;
int ->
<<"decode.int"/utf8>>;
float ->
<<"decode.float"/utf8>>;
bool ->
<<"decode.bool"/utf8>>;
string ->
<<"decode.string"/utf8>>;
{option, Type_@2} ->
<<<<"decode.optional("/utf8,
(gleam_type_to_decoder(Type_@2))/binary>>/binary,
")"/utf8>>;
json ->
<<"decode.string"/utf8>>
end.
-spec gleam_type_to_encoder(squirrel@internal@gleam:type(), binary()) -> binary().
gleam_type_to_encoder(Type_, Name) ->
case Type_ of
{list, Type_@1} ->
<<<<<<<<"pgo.array(list.map("/utf8, Name/binary>>/binary,
", fn(a) {"/utf8>>/binary,
(gleam_type_to_encoder(Type_@1, <<"a"/utf8>>))/binary>>/binary,
"}))"/utf8>>;
{option, Type_@2} ->
<<<<<<<<"pgo.nullable(fn(a) {"/utf8,
(gleam_type_to_encoder(Type_@2, <<"a"/utf8>>))/binary>>/binary,
"}, "/utf8>>/binary,
Name/binary>>/binary,
")"/utf8>>;
int ->
<<<<"pgo.int("/utf8, Name/binary>>/binary, ")"/utf8>>;
float ->
<<<<"pgo.float("/utf8, Name/binary>>/binary, ")"/utf8>>;
bool ->
<<<<"pgo.bool("/utf8, Name/binary>>/binary, ")"/utf8>>;
string ->
<<<<"pgo.text("/utf8, Name/binary>>/binary, ")"/utf8>>;
json ->
<<<<"pgo.text(json.to_string("/utf8, Name/binary>>/binary,
"))"/utf8>>
end.
-spec string(binary()) -> glam@doc:document().
string(Content) ->
Escaped_string = begin
_pipe = Content,
_pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>),
glam@doc:from_string(_pipe@2)
end,
_pipe@3 = [glam@doc:from_string(<<"\""/utf8>>),
Escaped_string,
glam@doc:from_string(<<"\""/utf8>>)],
glam@doc:concat(_pipe@3).
-spec append_if(list(SJC), boolean(), SJC) -> list(SJC).
append_if(List, Cond, Value) ->
case Cond of
true ->
[Value | List];
false ->
List
end.
-spec var(binary(), glam@doc:document()) -> glam@doc:document().
var(Name, Body) ->
_pipe@3 = [glam@doc:from_string(
<<<<"let "/utf8, Name/binary>>/binary, " ="/utf8>>
),
begin
_pipe = [{break, <<" "/utf8>>, <<""/utf8>>}, Body],
_pipe@1 = glam@doc:concat(_pipe),
_pipe@2 = glam@doc:group(_pipe@1),
glam@doc:nest(_pipe@2, 2)
end],
glam@doc:concat(_pipe@3).
-spec block(list(glam@doc:document())) -> glam@doc:document().
block(Body) ->
_pipe@2 = [glam@doc:from_string(<<"{"/utf8>>),
begin
_pipe = [{line, 1} | Body],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:nest(_pipe@1, 2)
end,
{line, 1},
glam@doc:from_string(<<"}"/utf8>>)],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:force_break(_pipe@3).
-spec call_block(binary(), list(glam@doc:document())) -> glam@doc:document().
call_block(Function, Body) ->
_pipe = [glam@doc:from_string(<<Function/binary, "("/utf8>>),
block(Body),
glam@doc:from_string(<<")"/utf8>>)],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1).
-spec comma_list(binary(), list(glam@doc:document()), binary()) -> glam@doc:document().
comma_list(Open, Content, Close) ->
_pipe@3 = [glam@doc:from_string(Open),
begin
_pipe = [{break, <<""/utf8>>, <<""/utf8>>},
glam@doc:join(
Content,
glam@doc:break(<<", "/utf8>>, <<","/utf8>>)
)],
_pipe@1 = glam@doc:concat(_pipe),
_pipe@2 = glam@doc:group(_pipe@1),
glam@doc:nest(_pipe@2, 2)
end,
glam@doc:break(<<""/utf8>>, <<","/utf8>>),
glam@doc:from_string(Close)],
_pipe@4 = glam@doc:concat(_pipe@3),
glam@doc:group(_pipe@4).
-spec list(list(glam@doc:document())) -> glam@doc:document().
list(Elems) ->
comma_list(<<"["/utf8>>, Elems, <<"]"/utf8>>).
-spec do_take_comment(binary(), list(binary())) -> list(binary()).
do_take_comment(Query, Lines) ->
case gleam@string:trim_left(Query) of
<<"--"/utf8, Rest/binary>> ->
case gleam@string:split_once(Rest, <<"\n"/utf8>>) of
{ok, {Line, Rest@1}} ->
do_take_comment(Rest@1, [gleam@string:trim(Line) | Lines]);
_ ->
do_take_comment(
<<""/utf8>>,
[gleam@string:trim(Rest) | Lines]
)
end;
_ ->
lists:reverse(Lines)
end.
-spec take_comment(binary()) -> list(binary()).
take_comment(Query) ->
do_take_comment(Query, []).
-spec from_file(binary()) -> {ok, untyped_query()} |
{error, squirrel@internal@error:error()}.
from_file(File) ->
Read_file = begin
_pipe = simplifile:read(File),
gleam@result:map_error(
_pipe,
fun(_capture) -> {cannot_read_file, File, _capture} end
)
end,
gleam@result:'try'(
Read_file,
fun(Content) ->
File_name = begin
_pipe@1 = filepath:base_name(File),
filepath:strip_extension(_pipe@1)
end,
Name = begin
_pipe@2 = squirrel@internal@gleam:identifier(File_name),
gleam@result:map_error(
_pipe@2,
fun(_capture@1) ->
{query_file_has_invalid_name,
File,
begin
_pipe@3 = squirrel@internal@gleam:similar_identifier_string(
File_name
),
gleam@option:from_result(_pipe@3)
end,
_capture@1}
end
)
end,
gleam@result:'try'(
Name,
fun(Name@1) ->
{ok,
{untyped_query,
File,
1,
Name@1,
take_comment(Content),
Content}}
end
)
end
).
-spec 'fun'(binary(), list(binary()), list(glam@doc:document())) -> glam@doc:document().
'fun'(Name, Args, Body) ->
Args@1 = gleam@list:map(Args, fun glam@doc:from_string/1),
_pipe@1 = [glam@doc:from_string(<<"pub fn "/utf8, Name/binary>>),
comma_list(<<"("/utf8>>, Args@1, <<") "/utf8>>),
block(
[begin
_pipe = Body,
glam@doc:join(_pipe, glam@doc:lines(2))
end]
),
{line, 1}],
_pipe@2 = glam@doc:concat(_pipe@1),
glam@doc:group(_pipe@2).
-spec call(binary(), list(glam@doc:document())) -> glam@doc:document().
call(Function, Args) ->
_pipe = [glam@doc:from_string(Function),
comma_list(<<"("/utf8>>, Args, <<")"/utf8>>)],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1).
-spec gleam_type_to_field_type(squirrel@internal@gleam:type()) -> glam@doc:document().
gleam_type_to_field_type(Type_) ->
case Type_ of
{list, Type_@1} ->
call(<<"List"/utf8>>, [gleam_type_to_field_type(Type_@1)]);
{option, Type_@2} ->
call(<<"Option"/utf8>>, [gleam_type_to_field_type(Type_@2)]);
int ->
glam@doc:from_string(<<"Int"/utf8>>);
float ->
glam@doc:from_string(<<"Float"/utf8>>);
bool ->
glam@doc:from_string(<<"Bool"/utf8>>);
string ->
glam@doc:from_string(<<"String"/utf8>>);
json ->
glam@doc:from_string(<<"String"/utf8>>)
end.
-spec record(binary(), list(squirrel@internal@gleam:field())) -> glam@doc:document().
record(Name, Fields) ->
Fields@1 = gleam@list:map(
Fields,
fun(Field) ->
Label = squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field)
),
_pipe = [glam@doc:from_string(<<Label/binary, ": "/utf8>>),
gleam_type_to_field_type(erlang:element(3, Field))],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1)
end
),
_pipe@4 = [glam@doc:from_string(
<<<<"pub type "/utf8, Name/binary>>/binary, " {"/utf8>>
),
begin
_pipe@2 = [{line, 1}, call(Name, Fields@1)],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:nest(_pipe@3, 2)
end,
{line, 1},
glam@doc:from_string(<<"}"/utf8>>)],
_pipe@5 = glam@doc:concat(_pipe@4),
glam@doc:group(_pipe@5).
-spec record_doc(binary(), binary(), typed_query()) -> {ok, glam@doc:document()} |
{error, nil}.
record_doc(Version, Constructor_name, Query) ->
{typed_query, File, _, Name, _, _, _, Returns} = Query,
gleam@bool:guard(
Returns =:= [],
{error, nil},
fun() ->
Function_name = squirrel@internal@gleam:identifier_to_string(Name),
Record_doc = <<<<<<<<<<<<"/// A row you get from running the `"/utf8,
Function_name/binary>>/binary,
"` query
/// defined in `"/utf8>>/binary,
File/binary>>/binary,
"`.
///
/// > 🐿️ This type definition was generated automatically using "/utf8>>/binary,
Version/binary>>/binary,
" of the
/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
///"/utf8>>,
_pipe = [glam@doc:from_string(Record_doc),
{line, 1},
record(Constructor_name, Returns)],
_pipe@1 = glam@doc:concat(_pipe),
{ok, _pipe@1}
end
).
-spec pipe_call(binary(), glam@doc:document(), list(glam@doc:document())) -> glam@doc:document().
pipe_call(Function, First, Rest) ->
_pipe = [First, {line, 1}, call(<<"|> "/utf8, Function/binary>>, Rest)],
glam@doc:concat(_pipe).
-spec decoder(binary(), list(squirrel@internal@gleam:field())) -> glam@doc:document().
decoder(Constructor, Returns) ->
gleam@bool:guard(
Returns =:= [],
glam@doc:from_string(
<<"decode.map(decode.dynamic, fn(_) { Nil })"/utf8>>
),
fun() ->
Parameters = gleam@list:map(
Returns,
fun(Field) ->
Label = squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field)
),
glam@doc:from_string(
<<<<"use "/utf8, Label/binary>>/binary,
" <- decode.parameter"/utf8>>
)
end
),
Pipes = gleam@list:index_map(
Returns,
fun(Field@1, I) ->
Position = begin
_pipe = gleam@int:to_string(I),
glam@doc:from_string(_pipe)
end,
Decoder = begin
_pipe@1 = gleam_type_to_decoder(
erlang:element(3, Field@1)
),
glam@doc:from_string(_pipe@1)
end,
call(<<"|> decode.field"/utf8>>, [Position, Decoder])
end
),
Labelled_names = gleam@list:map(
Returns,
fun(Field@2) ->
Label@1 = squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field@2)
),
glam@doc:from_string(
<<<<Label@1/binary, ": "/utf8>>/binary, Label@1/binary>>
)
end
),
_pipe@2 = [call_block(
<<"decode.into"/utf8>>,
[glam@doc:join(Parameters, {line, 1}),
{line, 1},
call(Constructor, Labelled_names)]
),
{line, 1},
glam@doc:join(Pipes, {line, 1})],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:group(_pipe@3)
end
).
-spec generate_code(binary(), typed_query()) -> {binary(),
gleam@set:set(binary())}.
generate_code(Version, Query) ->
{typed_query, File, _, Name, Comment, Content, Params, Returns} = Query,
Arg_name = fun(I) ->
<<"arg_"/utf8, (gleam@int:to_string(I + 1))/binary>>
end,
Inputs = gleam@list:index_map(Params, fun(_, I@1) -> Arg_name(I@1) end),
Inputs_encoders = gleam@list:index_map(
Params,
fun(P, I@2) -> _pipe = gleam_type_to_encoder(P, Arg_name(I@2)),
glam@doc:from_string(_pipe) end
),
Inputs_have_json = gleam@list:any(
Params,
fun squirrel@internal@gleam:contains_json/1
),
Function_name = squirrel@internal@gleam:identifier_to_string(Name),
Constructor_name = <<(squirrel@internal@gleam:identifier_to_type_name(Name))/binary,
"Row"/utf8>>,
Constructor_has_option = gleam@list:any(
Returns,
fun(Return) ->
squirrel@internal@gleam:contains_option(erlang:element(3, Return))
end
),
Fun_doc = case Comment of
[] ->
<<<<<<<<"/// Runs the `"/utf8, Function_name/binary>>/binary,
"` query
/// defined in `"/utf8>>/binary,
File/binary>>/binary,
"`."/utf8>>;
[_ | _] ->
_pipe@1 = gleam@list:map(
Comment,
fun(_capture) ->
gleam@string:append(<<"/// "/utf8>>, _capture)
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
Fun_doc@1 = <<<<<<Fun_doc/binary,
"
///
/// > 🐿️ This function was generated automatically using "/utf8>>/binary,
Version/binary>>/binary,
" of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///"/utf8>>,
Record@1 = case record_doc(Version, Constructor_name, Query) of
{ok, Record} ->
_pipe@2 = Record,
glam@doc:append(_pipe@2, glam@doc:lines(2));
{error, _} ->
{concat, []}
end,
Code = begin
_pipe@3 = [Record@1,
glam@doc:from_string(Fun_doc@1),
{line, 1},
'fun'(
Function_name,
[<<"db"/utf8>> | Inputs],
[var(<<"decoder"/utf8>>, decoder(Constructor_name, Returns)),
pipe_call(
<<"pgo.execute"/utf8>>,
string(Content),
[glam@doc:from_string(<<"db"/utf8>>),
list(Inputs_encoders),
glam@doc:from_string(
<<"decode.from(decoder, _)"/utf8>>
)]
)]
)],
_pipe@4 = glam@doc:concat(_pipe@3),
glam@doc:to_string(_pipe@4, 80)
end,
Imports = begin
_pipe@5 = [<<"import decode"/utf8>>, <<"import gleam/pgo"/utf8>>],
_pipe@6 = append_if(
_pipe@5,
Constructor_has_option,
<<"import gleam/option.{type Option}"/utf8>>
),
append_if(_pipe@6, Inputs_have_json, <<"import gleam/json"/utf8>>)
end,
{Code, gleam@set:from_list(Imports)}.