-module(squirrel@internal@query).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([add_types/3, from_file/1, generate_code/2]).
-export_type([untyped_query/0, typed_query/0, code_gen_state/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())}.
-type code_gen_state() :: {code_gen_state,
gleam@dict:dict(binary(), gleam@set:set(binary())),
boolean(),
boolean(),
boolean()}.
-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 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 function_doc(binary(), typed_query()) -> binary().
function_doc(Version, Query) ->
{typed_query, File, _, Name, Comment, _, _, _} = Query,
Function_name = squirrel@internal@gleam:identifier_to_string(Name),
Base = case Comment of
[] ->
<<<<<<<<"/// Runs the `"/utf8, Function_name/binary>>/binary,
"` query
/// defined in `"/utf8>>/binary,
File/binary>>/binary,
"`."/utf8>>;
[_ | _] ->
_pipe = gleam@list:map(
Comment,
fun(_capture) ->
gleam@string:append(<<"/// "/utf8>>, _capture)
end
),
gleam@string:join(_pipe, <<"\n"/utf8>>)
end,
<<<<< 🐿️ This function was generated automatically using "/utf8>>/binary,
Version/binary>>/binary,
" of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///"/utf8>>.
-spec string_doc(binary()) -> glam@doc:document().
string_doc(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 import_module(code_gen_state(), binary()) -> code_gen_state().
import_module(State, Name) ->
Imports = case gleam@dict:has_key(erlang:element(2, State), Name) of
false ->
gleam@dict:insert(erlang:element(2, State), Name, gleam@set:new());
true ->
erlang:element(2, State)
end,
erlang:setelement(2, State, Imports).
-spec default_codegen_state() -> code_gen_state().
default_codegen_state() ->
_pipe = {code_gen_state, gleam@dict:new(), false, false, false},
_pipe@1 = import_module(_pipe, <<"decode"/utf8>>),
import_module(_pipe@1, <<"gleam/pgo"/utf8>>).
-spec import_qualified(code_gen_state(), binary(), binary()) -> code_gen_state().
import_qualified(State, Module, Imported) ->
Imports = gleam@dict:upsert(
erlang:element(2, State),
Module,
fun(Imported_values) -> case Imported_values of
{some, Imported_values@1} ->
gleam@set:insert(Imported_values@1, Imported);
none ->
gleam@set:from_list([Imported])
end end
),
erlang:setelement(2, State, Imports).
-spec prepend_if(list(STG), boolean(), STG) -> list(STG).
prepend_if(List, Condition, Item) ->
case Condition of
true ->
[Item | List];
false ->
List
end.
-spec imports_doc(gleam@dict:dict(binary(), gleam@set:set(binary()))) -> glam@doc:document().
imports_doc(Imports) ->
Sorted_imports = begin
_pipe = maps:to_list(Imports),
gleam@list:sort(
_pipe,
fun(One, Other) ->
gleam@string:compare(
erlang:element(1, One),
erlang:element(1, Other)
)
end
)
end,
_pipe@9 = (gleam@list:map(
Sorted_imports,
fun(_use0) ->
{Module, Imported_values} = _use0,
Import_line = glam@doc:from_string(
<<"import "/utf8, Module/binary>>
),
gleam@bool:guard(
gleam@set:is_empty(Imported_values),
Import_line,
fun() ->
Imported_values@1 = begin
_pipe@1 = gleam@set:to_list(Imported_values),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun gleam@string:compare/2
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun glam@doc:from_string/1
),
_pipe@4 = glam@doc:join(
_pipe@3,
glam@doc:break(<<", "/utf8>>, <<","/utf8>>)
),
glam@doc:group(_pipe@4)
end,
_pipe@8 = [Import_line,
glam@doc:from_string(<<".{"/utf8>>),
begin
_pipe@5 = [{break, <<""/utf8>>, <<""/utf8>>},
Imported_values@1],
_pipe@6 = glam@doc:concat(_pipe@5),
_pipe@7 = glam@doc:group(_pipe@6),
glam@doc:nest(_pipe@7, 2)
end,
{break, <<""/utf8>>, <<""/utf8>>},
glam@doc:from_string(<<"}"/utf8>>)],
glam@doc:concat(_pipe@8)
end
)
end
)),
glam@doc:join(_pipe@9, {line, 1}).
-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(<>),
block(Body),
glam@doc:from_string(<<")"/utf8>>)],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1).
-spec var_doc(binary(), glam@doc:document()) -> glam@doc:document().
var_doc(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 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 call_doc(binary(), list(glam@doc:document())) -> glam@doc:document().
call_doc(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_decoder(code_gen_state(), squirrel@internal@gleam:type()) -> {code_gen_state(),
glam@doc:document()}.
gleam_type_to_decoder(State, Type_) ->
case Type_ of
uuid ->
State@1 = begin
_pipe = erlang:setelement(3, State, true),
import_module(_pipe, <<"youid/uuid"/utf8>>)
end,
{State@1, glam@doc:from_string(<<"uuid_decoder()"/utf8>>)};
{list, Type_@1} ->
{State@2, Inner_decoder} = gleam_type_to_decoder(State, Type_@1),
{State@2, call_doc(<<"decode.list"/utf8>>, [Inner_decoder])};
{option, Type_@2} ->
{State@3, Inner_decoder@1} = gleam_type_to_decoder(State, Type_@2),
{State@3, call_doc(<<"decode.optional"/utf8>>, [Inner_decoder@1])};
date ->
State@4 = erlang:setelement(4, State, true),
{State@4, glam@doc:from_string(<<"date_decoder()"/utf8>>)};
timestamp ->
State@5 = erlang:setelement(5, State, true),
{State@5, glam@doc:from_string(<<"timestamp_decoder()"/utf8>>)};
int ->
{State, glam@doc:from_string(<<"decode.int"/utf8>>)};
float ->
{State, glam@doc:from_string(<<"decode.float"/utf8>>)};
bool ->
{State, glam@doc:from_string(<<"decode.bool"/utf8>>)};
string ->
{State, glam@doc:from_string(<<"decode.string"/utf8>>)};
bit_array ->
{State, glam@doc:from_string(<<"decode.bit_array"/utf8>>)};
json ->
{State, glam@doc:from_string(<<"decode.string"/utf8>>)}
end.
-spec gleam_type_to_field_type(code_gen_state(), squirrel@internal@gleam:type()) -> {code_gen_state(),
glam@doc:document()}.
gleam_type_to_field_type(State, Type_) ->
case Type_ of
{list, Type_@1} ->
{State@1, Inner_type} = gleam_type_to_field_type(State, Type_@1),
{State@1, call_doc(<<"List"/utf8>>, [Inner_type])};
{option, Type_@2} ->
State@2 = begin
_pipe = State,
import_qualified(
_pipe,
<<"gleam/option"/utf8>>,
<<"type Option"/utf8>>
)
end,
{State@3, Inner_type@1} = gleam_type_to_field_type(State@2, Type_@2),
{State@3, call_doc(<<"Option"/utf8>>, [Inner_type@1])};
uuid ->
{begin
_pipe@1 = State,
import_qualified(
_pipe@1,
<<"youid/uuid"/utf8>>,
<<"type Uuid"/utf8>>
)
end,
glam@doc:from_string(<<"Uuid"/utf8>>)};
date ->
{State, glam@doc:from_string(<<"#(Int, Int, Int)"/utf8>>)};
timestamp ->
{State,
glam@doc:from_string(
<<"#(#(Int, Int, Int), #(Int, Int, Int))"/utf8>>
)};
int ->
{State, glam@doc:from_string(<<"Int"/utf8>>)};
float ->
{State, glam@doc:from_string(<<"Float"/utf8>>)};
bool ->
{State, glam@doc:from_string(<<"Bool"/utf8>>)};
string ->
{State, glam@doc:from_string(<<"String"/utf8>>)};
json ->
{State, glam@doc:from_string(<<"String"/utf8>>)};
bit_array ->
{State, glam@doc:from_string(<<"BitArray"/utf8>>)}
end.
-spec record_doc(code_gen_state(), binary(), binary(), typed_query()) -> {ok,
{code_gen_state(), glam@doc:document()}} |
{error, nil}.
record_doc(State, Version, Type_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>>,
{State@3, Fields@1} = (gleam@list:fold(
Returns,
{State, []},
fun(_use0, Field) ->
{State@1, Fields} = _use0,
Label = glam@doc:from_string(
<<(squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field)
))/binary,
": "/utf8>>
),
{State@2, Field_type} = gleam_type_to_field_type(
State@1,
erlang:element(3, Field)
),
Field@1 = begin
_pipe = [Label, Field_type],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:group(_pipe@1)
end,
{State@2, [Field@1 | Fields]}
end
)),
Fields@2 = lists:reverse(Fields@1),
Result = begin
_pipe@6 = [glam@doc:from_string(Record_doc),
{line, 1},
begin
_pipe@4 = [glam@doc:from_string(
<<<<"pub type "/utf8, Type_name/binary>>/binary,
" {"/utf8>>
),
begin
_pipe@2 = [{line, 1},
call_doc(Type_name, Fields@2)],
_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)
end],
glam@doc:concat(_pipe@6)
end,
{ok, {State@3, Result}}
end
).
-spec pipe_call_doc(binary(), glam@doc:document(), list(glam@doc:document())) -> glam@doc:document().
pipe_call_doc(Function, First, Rest) ->
_pipe = [First, {line, 1}, call_doc(<<"|> "/utf8, Function/binary>>, Rest)],
glam@doc:concat(_pipe).
-spec fun_doc(binary(), list(binary()), list(glam@doc:document())) -> glam@doc:document().
fun_doc(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 fn_doc(list(binary()), glam@doc:document()) -> glam@doc:document().
fn_doc(Args, Body) ->
_pipe@2 = [glam@doc:from_string(<<"fn"/utf8>>),
comma_list(
<<"("/utf8>>,
gleam@list:map(Args, fun glam@doc:from_string/1),
<<") {"/utf8>>
),
begin
_pipe = [{break, <<" "/utf8>>, <<""/utf8>>}, Body],
_pipe@1 = glam@doc:concat(_pipe),
glam@doc:nest(_pipe@1, 2)
end,
{break, <<" "/utf8>>, <<""/utf8>>},
glam@doc:from_string(<<"}"/utf8>>)],
_pipe@3 = glam@doc:concat(_pipe@2),
glam@doc:group(_pipe@3).
-spec gleam_type_to_encoder(
code_gen_state(),
squirrel@internal@gleam:type(),
binary()
) -> {code_gen_state(), glam@doc:document()}.
gleam_type_to_encoder(State, Type_, Name) ->
Name@1 = glam@doc:from_string(Name),
case Type_ of
{list, Type_@1} ->
State@1 = begin
_pipe = State,
import_module(_pipe, <<"gleam/list"/utf8>>)
end,
{State@2, Inner_encoder} = gleam_type_to_encoder(
State@1,
Type_@1,
<<"value"/utf8>>
),
Map_fn = fn_doc([<<"value"/utf8>>], Inner_encoder),
Doc = call_doc(
<<"pgo.array"/utf8>>,
[call_doc(<<"list.map"/utf8>>, [Name@1, Map_fn])]
),
{State@2, Doc};
{option, Type_@2} ->
{State@3, Inner_encoder@1} = gleam_type_to_encoder(
State,
Type_@2,
<<"value"/utf8>>
),
Doc@1 = call_doc(
<<"pgo.nullable"/utf8>>,
[fn_doc([<<"value"/utf8>>], Inner_encoder@1), Name@1]
),
{State@3, Doc@1};
uuid ->
State@4 = begin
_pipe@1 = State,
import_module(_pipe@1, <<"youid/uuid"/utf8>>)
end,
Doc@2 = call_doc(
<<"pgo.text"/utf8>>,
[call_doc(<<"uuid.to_string"/utf8>>, [Name@1])]
),
{State@4, Doc@2};
json ->
State@5 = begin
_pipe@2 = State,
import_module(_pipe@2, <<"gleam/json"/utf8>>)
end,
Doc@3 = call_doc(
<<"pgo.text"/utf8>>,
[call_doc(<<"json.to_string"/utf8>>, [Name@1])]
),
{State@5, Doc@3};
date ->
{State, call_doc(<<"pgo.date"/utf8>>, [Name@1])};
timestamp ->
{State, call_doc(<<"pgo.timestamp"/utf8>>, [Name@1])};
int ->
{State, call_doc(<<"pgo.int"/utf8>>, [Name@1])};
float ->
{State, call_doc(<<"pgo.float"/utf8>>, [Name@1])};
bool ->
{State, call_doc(<<"pgo.bool"/utf8>>, [Name@1])};
string ->
{State, call_doc(<<"pgo.text"/utf8>>, [Name@1])};
bit_array ->
{State, call_doc(<<"pgo.bytea"/utf8>>, [Name@1])}
end.
-spec list_doc(list(glam@doc:document())) -> glam@doc:document().
list_doc(Elems) ->
comma_list(<<"["/utf8>>, Elems, <<"]"/utf8>>).
-spec decoder_doc(
code_gen_state(),
binary(),
list(squirrel@internal@gleam:field())
) -> {code_gen_state(), glam@doc:document()}.
decoder_doc(State, Constructor, Returns) ->
Fallback = {State,
glam@doc:from_string(
<<"decode.map(decode.dynamic, fn(_) { Nil })"/utf8>>
)},
gleam@bool:guard(
Returns =:= [],
Fallback,
fun() ->
{State@3, Parameters@2, Labelled_names@2, Pipes@2} = begin
Acc = {State, [], [], []},
gleam@list:index_fold(
Returns,
Acc,
fun(Acc@1, Field, I) ->
{State@1, Parameters, Labelled_names, Pipes} = Acc@1,
Label = squirrel@internal@gleam:identifier_to_string(
erlang:element(2, Field)
),
Param = glam@doc:from_string(
<<<<"use "/utf8, Label/binary>>/binary,
" <- decode.parameter"/utf8>>
),
Parameters@1 = [Param | Parameters],
Labelled_name = glam@doc:from_string(
<<<