-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()}. -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}, _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(SSU), boolean(), SSU) -> list(SSU). 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>>)}; 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>>)}; 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])}; 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( <<<