Packages

Generate typed Gleam modules from a SQL schema file

Current section

Files

Jump to
glsql src glsql@internal@codegen.erl
Raw

src/glsql@internal@codegen.erl

-module(glsql@internal@codegen).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glsql/internal/codegen.gleam").
-export([module_prefix/1, generate_schema_module/1, generate_table/4]).
-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).
-file("src/glsql/internal/codegen.gleam", 15).
?DOC(false).
-spec module_prefix(binary()) -> binary().
module_prefix(Out_dir) ->
Inner = begin
_pipe = Out_dir,
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
_pipe@2 = gleam@list:fold(_pipe@1, [], fun(Acc, Part) -> case Part of
<<"src"/utf8>> ->
[];
<<"test"/utf8>> ->
[];
<<"dev"/utf8>> ->
[];
<<""/utf8>> ->
Acc;
_ ->
[Part | Acc]
end end),
_pipe@3 = lists:reverse(_pipe@2),
gleam@string:join(_pipe@3, <<"/"/utf8>>)
end,
case Inner of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
<<Inner/binary, "/"/utf8>>
end.
-file("src/glsql/internal/codegen.gleam", 38).
?DOC(false).
-spec generate_schema_module(binary()) -> binary().
generate_schema_module(Source) ->
<<<<<<<<"// "/utf8, "glsql_schema"/utf8>>/binary,
".gleam\n// Generated by glsql from "/utf8>>/binary,
Source/binary>>/binary,
". Do not edit.\n\nimport gleam/dynamic/decode\n\n/// A column descriptor. The `value` type parameter records the Gleam type\n/// of the column, and `decoder` decodes just that column, so a query that\n/// selects fewer than all of a table's columns can still be decoded type\n/// safely by composing the pieces it needs.\npub type Column(value) {\n Column(\n table: String,\n name: String,\n sql_type: String,\n nullable: Bool,\n primary_key: Bool,\n decoder: decode.Decoder(value),\n )\n}\n"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 332).
?DOC(false).
-spec bool_literal(boolean()) -> binary().
bool_literal(Value) ->
case Value of
true ->
<<"True"/utf8>>;
false ->
<<"False"/utf8>>
end.
-file("src/glsql/internal/codegen.gleam", 328).
?DOC(false).
-spec quoted(binary()) -> binary().
quoted(Value) ->
<<<<"\""/utf8, Value/binary>>/binary, "\""/utf8>>.
-file("src/glsql/internal/codegen.gleam", 113).
?DOC(false).
-spec call(binary(), binary(), list(binary()), binary()) -> binary().
call(Indent, Open, Args, Close) ->
One_line = <<<<<<Indent/binary, Open/binary>>/binary,
(gleam@string:join(Args, <<", "/utf8>>))/binary>>/binary,
Close/binary>>,
case string:length(One_line) =< 80 of
true ->
One_line;
false ->
Lines = begin
_pipe = Args,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) ->
<<<<<<Indent/binary, " "/utf8>>/binary, A/binary>>/binary,
","/utf8>>
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<<<<<<<<<Indent/binary, Open/binary>>/binary, "\n"/utf8>>/binary,
Lines/binary>>/binary,
"\n"/utf8>>/binary,
Indent/binary>>/binary,
Close/binary>>
end.
-file("src/glsql/internal/codegen.gleam", 298).
?DOC(false).
-spec column_accessor(
glsql@internal@resolve:resolved_table(),
glsql@internal@resolve:resolved_column()
) -> binary().
column_accessor(Table, C) ->
Signature = <<(call(
<<""/utf8>>,
<<<<"pub fn col_"/utf8, (erlang:element(3, C))/binary>>/binary,
"() -> Column("/utf8>>,
[erlang:element(4, C)],
<<")"/utf8>>
))/binary,
" {"/utf8>>,
Body = call(
<<" "/utf8>>,
<<"Column("/utf8>>,
[quoted(erlang:element(2, Table)),
quoted(erlang:element(2, C)),
quoted(erlang:element(7, C)),
bool_literal(erlang:element(8, C)),
bool_literal(erlang:element(9, C)),
erlang:element(5, C)],
<<")"/utf8>>
),
<<<<<<Signature/binary, "\n"/utf8>>/binary, Body/binary>>/binary,
"\n}"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 291).
?DOC(false).
-spec metadata(glsql@internal@resolve:resolved_table()) -> binary().
metadata(Table) ->
_pipe = erlang:element(5, Table),
_pipe@1 = gleam@list:map(_pipe, fun(C) -> column_accessor(Table, C) end),
_pipe@2 = gleam@string:join(_pipe@1, <<"\n\n"/utf8>>),
gleam@string:append(_pipe@2, <<"\n"/utf8>>).
-file("src/glsql/internal/codegen.gleam", 284).
?DOC(false).
-spec split_encoder(binary()) -> {binary(), binary()}.
split_encoder(Encoder) ->
case gleam@string:split(Encoder, <<"$"/utf8>>) of
[Before, After] ->
{Before, After};
_ ->
{Encoder, <<""/utf8>>}
end.
-file("src/glsql/internal/codegen.gleam", 243).
?DOC(false).
-spec encode_value(glsql@internal@resolve:resolved_column()) -> binary().
encode_value(C) ->
Is_list = gleam_stdlib:contains_string(
erlang:element(4, C),
<<"List("/utf8>>
),
Substitute = fun(Var) ->
gleam@string:replace(erlang:element(6, C), <<"$"/utf8>>, Var)
end,
Inline_value = fun(Var@1) -> case Is_list of
true ->
<<<<<<<<"pog.array(fn(v) { "/utf8,
(Substitute(<<"v"/utf8>>))/binary>>/binary,
" }, "/utf8>>/binary,
Var@1/binary>>/binary,
")"/utf8>>;
false ->
Substitute(Var@1)
end end,
Row = <<"row."/utf8, (erlang:element(3, C))/binary>>,
Body = case {erlang:element(8, C), Is_list} of
{true, _} ->
call(
<<" "/utf8>>,
<<"pog.nullable("/utf8>>,
[<<<<"fn(v) { "/utf8, (Inline_value(<<"v"/utf8>>))/binary>>/binary,
" }"/utf8>>,
Row],
<<")"/utf8>>
);
{false, true} ->
call(
<<" "/utf8>>,
<<"pog.array("/utf8>>,
[<<<<"fn(v) { "/utf8, (Substitute(<<"v"/utf8>>))/binary>>/binary,
" }"/utf8>>,
Row],
<<")"/utf8>>
);
{false, false} ->
{Open, Close} = split_encoder(erlang:element(6, C)),
call(<<" "/utf8>>, Open, [Row], Close)
end,
<<Body/binary, ","/utf8>>.
-file("src/glsql/internal/codegen.gleam", 221).
?DOC(false).
-spec encoder(glsql@internal@resolve:resolved_table(), binary()) -> binary().
encoder(Table, Driver) ->
Values = begin
_pipe = erlang:element(5, Table),
_pipe@1 = gleam@list:map(_pipe, fun encode_value/1),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<<<(call(
<<""/utf8>>,
<<"pub fn to_params("/utf8>>,
[<<"row: "/utf8, (erlang:element(4, Table))/binary>>],
<<<<") -> List("/utf8, Driver/binary>>/binary,
".Value) {"/utf8>>
))/binary,
"\n [\n"/utf8>>/binary,
Values/binary>>/binary,
"\n ]\n}\n"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 196).
?DOC(false).
-spec decoder(glsql@internal@resolve:resolved_table()) -> binary().
decoder(Table) ->
Fields = begin
_pipe = erlang:element(5, Table),
_pipe@1 = gleam@list:index_map(
_pipe,
fun(C, I) ->
call(
<<" "/utf8>>,
<<<<"use "/utf8, (erlang:element(3, C))/binary>>/binary,
" <- decode.field("/utf8>>,
[erlang:integer_to_binary(I), erlang:element(5, C)],
<<")"/utf8>>
)
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
Labels = gleam@list:map(
erlang:element(5, Table),
fun(C@1) -> <<(erlang:element(3, C@1))/binary, ":"/utf8>> end
),
<<<<<<<<<<(call(
<<""/utf8>>,
<<"pub fn decoder() -> decode.Decoder("/utf8>>,
[erlang:element(4, Table)],
<<")"/utf8>>
))/binary,
" {\n"/utf8>>/binary,
Fields/binary>>/binary,
"\n"/utf8>>/binary,
(call(
<<" "/utf8>>,
<<<<"decode.success("/utf8, (erlang:element(4, Table))/binary>>/binary,
"("/utf8>>,
Labels,
<<")"/utf8>>
))/binary>>/binary,
")\n}\n"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 183).
?DOC(false).
-spec constants(glsql@internal@resolve:resolved_table()) -> binary().
constants(Table) ->
Names = begin
_pipe = erlang:element(5, Table),
_pipe@1 = gleam@list:map(_pipe, fun(C) -> erlang:element(2, C) end),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
<<<<<<<<"pub const table: String = \""/utf8,
(erlang:element(2, Table))/binary>>/binary,
"\"\n\n/// Columns in declaration order. Pair with `decoder()` so the SELECT\n/// order always matches the positional decoder.\npub const columns: String = \""/utf8>>/binary,
Names/binary>>/binary,
"\"\n"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 176).
?DOC(false).
-spec without_last_paren(binary()) -> binary().
without_last_paren(Text) ->
case gleam_stdlib:string_ends_with(Text, <<")"/utf8>>) of
true ->
gleam@string:slice(Text, 0, string:length(Text) - 1);
false ->
Text
end.
-file("src/glsql/internal/codegen.gleam", 155).
?DOC(false).
-spec record_field(glsql@internal@resolve:resolved_column()) -> binary().
record_field(C) ->
One_line = <<<<<<" "/utf8, (erlang:element(3, C))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(4, C))/binary>>,
case string:length(One_line) =< 80 of
true ->
<<One_line/binary, ","/utf8>>;
false ->
case gleam@string:split_once(erlang:element(4, C), <<"("/utf8>>) of
{ok, {Wrapper, Rest}} ->
<<<<<<<<<<<<" "/utf8, (erlang:element(3, C))/binary>>/binary,
": "/utf8>>/binary,
Wrapper/binary>>/binary,
"(\n "/utf8>>/binary,
(without_last_paren(Rest))/binary>>/binary,
",\n ),"/utf8>>;
{error, nil} ->
One_line
end
end.
-file("src/glsql/internal/codegen.gleam", 132).
?DOC(false).
-spec record(glsql@internal@resolve:resolved_table()) -> binary().
record(Table) ->
Fields = gleam@list:map(
erlang:element(5, Table),
fun(C) ->
<<<<(erlang:element(3, C))/binary, ": "/utf8>>/binary,
(erlang:element(4, C))/binary>>
end
),
One_line = <<<<<<<<" "/utf8, (erlang:element(4, Table))/binary>>/binary,
"("/utf8>>/binary,
(gleam@string:join(Fields, <<", "/utf8>>))/binary>>/binary,
")"/utf8>>,
Body = case string:length(One_line) =< 80 of
true ->
One_line;
false ->
Lines = begin
_pipe = erlang:element(5, Table),
_pipe@1 = gleam@list:map(_pipe, fun record_field/1),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<<<<<" "/utf8, (erlang:element(4, Table))/binary>>/binary,
"(\n"/utf8>>/binary,
Lines/binary>>/binary,
"\n )"/utf8>>
end,
<<<<<<<<"pub type "/utf8, (erlang:element(4, Table))/binary>>/binary,
" {\n"/utf8>>/binary,
Body/binary>>/binary,
"\n}\n"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 74).
?DOC(false).
-spec imports(glsql@internal@resolve:resolved_table(), binary(), binary()) -> binary().
imports(Table, Driver, Schema_import) ->
Needs_option = gleam@list:any(
erlang:element(5, Table),
fun(C) -> erlang:element(8, C) end
),
Base = [<<"gleam/dynamic/decode"/utf8>>, Schema_import, Driver],
Base@1 = case Needs_option of
true ->
[<<"gleam/option"/utf8>> | Base];
false ->
Base
end,
Custom = gleam@list:flat_map(
erlang:element(5, Table),
fun(C@1) -> erlang:element(10, C@1) end
),
All = begin
_pipe = lists:append(Base@1, Custom),
_pipe@1 = gleam@list:unique(_pipe),
gleam@list:sort(_pipe@1, fun gleam@string:compare/2)
end,
_pipe@2 = All,
_pipe@3 = gleam@list:map(_pipe@2, fun(M) -> case M of
<<"gleam/option"/utf8>> ->
<<"import gleam/option.{type Option}"/utf8>>;
_ when M =:= Schema_import ->
<<<<"import "/utf8, M/binary>>/binary,
".{type Column, Column}"/utf8>>;
_ ->
<<"import "/utf8, M/binary>>
end end),
_pipe@4 = gleam@string:join(_pipe@3, <<"\n"/utf8>>),
gleam@string:append(_pipe@4, <<"\n"/utf8>>).
-file("src/glsql/internal/codegen.gleam", 66).
?DOC(false).
-spec header(glsql@internal@resolve:resolved_table(), binary()) -> binary().
header(Table, Source) ->
<<<<<<<<"// "/utf8, (erlang:element(3, Table))/binary>>/binary,
".gleam\n// Generated by glsql from "/utf8>>/binary,
Source/binary>>/binary,
". Do not edit.\n"/utf8>>.
-file("src/glsql/internal/codegen.gleam", 46).
?DOC(false).
-spec generate_table(
glsql@internal@resolve:resolved_table(),
binary(),
binary(),
binary()
) -> binary().
generate_table(Table, Driver, Source, Schema_import) ->
gleam@string:join(
[header(Table, Source),
imports(Table, Driver, Schema_import),
record(Table),
constants(Table),
decoder(Table),
encoder(Table, Driver),
metadata(Table)],
<<"\n"/utf8>>
).