Current section
Files
Jump to
Current section
Files
src/cquill@cli@generate.erl
-module(cquill@cli@generate).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/cquill/cli/generate.gleam").
-export([run/1, watch/1]).
-export_type([generate_result/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type generate_result() :: {generate_success, integer()} |
{generate_error, binary()}.
-file("src/cquill/cli/generate.gleam", 163).
?DOC(" Mask password in database URL for display\n").
-spec mask_password(binary()) -> binary().
mask_password(Url) ->
case gleam@string:split(Url, <<"://"/utf8>>) of
[Protocol, Rest] ->
case gleam@string:split(Rest, <<"@"/utf8>>) of
[Auth, Host] ->
case gleam@string:split(Auth, <<":"/utf8>>) of
[User, _] ->
<<<<<<<<Protocol/binary, "://"/utf8>>/binary,
User/binary>>/binary,
":****@"/utf8>>/binary,
Host/binary>>;
_ ->
Url
end;
_ ->
Url
end;
_ ->
Url
end.
-file("src/cquill/cli/generate.gleam", 117).
?DOC(" Connect to the database using the provided URL\n").
-spec connect_to_database(cquill@cli@args:generate_options()) -> {ok,
cquill@adapter@postgres:postgres_connection()} |
{error, binary()}.
connect_to_database(Options) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(<<"Connecting to database..."/utf8>>);
false ->
nil
end,
Pool_name = gleam_erlang_ffi:new_name(<<"cquill_codegen"/utf8>>),
case cquill@adapter@postgres:config_from_url(
Pool_name,
erlang:element(2, Options)
) of
{error, _} ->
{error,
<<<<<<<<"Could not parse database URL\n\n"/utf8,
" URL: "/utf8>>/binary,
(mask_password(erlang:element(2, Options)))/binary>>/binary,
"\n\n"/utf8>>/binary,
" Expected format: postgres://user:password@host:port/database"/utf8>>};
{ok, Config} ->
Config@1 = begin
_pipe = Config,
_pipe@1 = cquill@adapter@postgres:pool_size(_pipe, 1),
cquill@adapter@postgres:default_timeout(_pipe@1, 30000)
end,
case cquill@adapter@postgres:start(Config@1) of
{error, _} ->
{error,
<<<<<<<<<<<<<<"Could not connect to database\n\n"/utf8,
" Connection refused: "/utf8>>/binary,
(mask_password(
erlang:element(2, Options)
))/binary>>/binary,
"\n\n"/utf8>>/binary,
" Make sure:\n"/utf8>>/binary,
" - PostgreSQL is running\n"/utf8>>/binary,
" - The database exists\n"/utf8>>/binary,
" - Credentials are correct"/utf8>>};
{ok, Conn} ->
{ok, Conn}
end
end.
-file("src/cquill/cli/generate.gleam", 267).
?DOC(" Execute an introspection query and parse results\n").
-spec execute_introspection_query(
cquill@adapter@postgres:postgres_connection(),
binary(),
list(cquill@adapter:query_param()),
fun((list(gleam@dynamic:dynamic_())) -> {ok, RBA} | {error, binary()}),
binary()
) -> {ok, list(RBA)} | {error, binary()}.
execute_introspection_query(Conn, Query, Params, Parser, Description) ->
gleam@result:'try'(
begin
_pipe = cquill@adapter@postgres:execute_sql(Conn, Query, Params),
gleam@result:map_error(
_pipe,
fun(_) ->
<<<<"Failed to query "/utf8, Description/binary>>/binary,
" from database"/utf8>>
end
)
end,
fun(Rows) -> _pipe@1 = Rows,
_pipe@2 = gleam@list:try_map(_pipe@1, Parser),
gleam@result:map_error(
_pipe@2,
fun(Err) ->
<<<<<<"Failed to parse "/utf8, Description/binary>>/binary,
": "/utf8>>/binary,
Err/binary>>
end
) end
).
-file("src/cquill/cli/generate.gleam", 452).
-spec decode_string(gleam@dynamic:dynamic_(), binary()) -> {ok, binary()} |
{error, binary()}.
decode_string(Value, Field) ->
case gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
{ok, S};
{error, _} ->
{error, <<"Expected string for "/utf8, Field/binary>>}
end.
-file("src/cquill/cli/generate.gleam", 360).
-spec parse_fk_row(list(gleam@dynamic:dynamic_())) -> {ok,
cquill@introspection:raw_foreign_key_row()} |
{error, binary()}.
parse_fk_row(Row) ->
case Row of
[Table_name,
Column_name,
Foreign_table_name,
Foreign_column_name,
Update_rule,
Delete_rule] ->
gleam@result:'try'(
decode_string(Table_name, <<"table_name"/utf8>>),
fun(Table_name@1) ->
gleam@result:'try'(
decode_string(Column_name, <<"column_name"/utf8>>),
fun(Column_name@1) ->
gleam@result:'try'(
decode_string(
Foreign_table_name,
<<"foreign_table_name"/utf8>>
),
fun(Foreign_table_name@1) ->
gleam@result:'try'(
decode_string(
Foreign_column_name,
<<"foreign_column_name"/utf8>>
),
fun(Foreign_column_name@1) ->
gleam@result:'try'(
decode_string(
Update_rule,
<<"update_rule"/utf8>>
),
fun(Update_rule@1) ->
gleam@result:'try'(
decode_string(
Delete_rule,
<<"delete_rule"/utf8>>
),
fun(Delete_rule@1) ->
{ok,
{raw_foreign_key_row,
Table_name@1,
Column_name@1,
Foreign_table_name@1,
Foreign_column_name@1,
Update_rule@1,
Delete_rule@1}}
end
)
end
)
end
)
end
)
end
)
end
);
_ ->
{error, <<"Invalid foreign key row format"/utf8>>}
end.
-file("src/cquill/cli/generate.gleam", 397).
-spec parse_unique_row(list(gleam@dynamic:dynamic_())) -> {ok,
cquill@introspection:raw_unique_row()} |
{error, binary()}.
parse_unique_row(Row) ->
case Row of
[Table_name, Constraint_name, Column_name] ->
gleam@result:'try'(
decode_string(Table_name, <<"table_name"/utf8>>),
fun(Table_name@1) ->
gleam@result:'try'(
decode_string(
Constraint_name,
<<"constraint_name"/utf8>>
),
fun(Constraint_name@1) ->
gleam@result:'try'(
decode_string(
Column_name,
<<"column_name"/utf8>>
),
fun(Column_name@1) ->
{ok,
{raw_unique_row,
Table_name@1,
Constraint_name@1,
Column_name@1}}
end
)
end
)
end
);
_ ->
{error, <<"Invalid unique constraint row format"/utf8>>}
end.
-file("src/cquill/cli/generate.gleam", 414).
-spec parse_check_row(list(gleam@dynamic:dynamic_())) -> {ok,
cquill@introspection:raw_check_row()} |
{error, binary()}.
parse_check_row(Row) ->
case Row of
[Table_name, Constraint_name, Check_clause] ->
gleam@result:'try'(
decode_string(Table_name, <<"table_name"/utf8>>),
fun(Table_name@1) ->
gleam@result:'try'(
decode_string(
Constraint_name,
<<"constraint_name"/utf8>>
),
fun(Constraint_name@1) ->
gleam@result:'try'(
decode_string(
Check_clause,
<<"check_clause"/utf8>>
),
fun(Check_clause@1) ->
{ok,
{raw_check_row,
Table_name@1,
Constraint_name@1,
Check_clause@1}}
end
)
end
)
end
);
_ ->
{error, <<"Invalid check constraint row format"/utf8>>}
end.
-file("src/cquill/cli/generate.gleam", 459).
-spec decode_int(gleam@dynamic:dynamic_(), binary()) -> {ok, integer()} |
{error, binary()}.
decode_int(Value, Field) ->
case gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, I} ->
{ok, I};
{error, _} ->
{error, <<"Expected int for "/utf8, Field/binary>>}
end.
-file("src/cquill/cli/generate.gleam", 339).
-spec parse_pk_row(list(gleam@dynamic:dynamic_())) -> {ok,
cquill@introspection:raw_primary_key_row()} |
{error, binary()}.
parse_pk_row(Row) ->
case Row of
[Table_name, Column_name, Ordinal_position] ->
gleam@result:'try'(
decode_string(Table_name, <<"table_name"/utf8>>),
fun(Table_name@1) ->
gleam@result:'try'(
decode_string(Column_name, <<"column_name"/utf8>>),
fun(Column_name@1) ->
gleam@result:'try'(
decode_int(
Ordinal_position,
<<"ordinal_position"/utf8>>
),
fun(Ordinal_position@1) ->
{ok,
{raw_primary_key_row,
Table_name@1,
Column_name@1,
Ordinal_position@1}}
end
)
end
)
end
);
_ ->
{error, <<"Invalid primary key row format"/utf8>>}
end.
-file("src/cquill/cli/generate.gleam", 466).
-spec decode_float(gleam@dynamic:dynamic_(), binary()) -> {ok, float()} |
{error, binary()}.
decode_float(Value, Field) ->
case gleam@dynamic@decode:run(
Value,
{decoder, fun gleam@dynamic@decode:decode_float/1}
) of
{ok, F} ->
{ok, F};
{error, _} ->
{error, <<"Expected float for "/utf8, Field/binary>>}
end.
-file("src/cquill/cli/generate.gleam", 431).
-spec parse_enum_row(list(gleam@dynamic:dynamic_())) -> {ok,
cquill@introspection:raw_enum_row()} |
{error, binary()}.
parse_enum_row(Row) ->
case Row of
[Enum_name, Enum_value, Enum_sort_order] ->
gleam@result:'try'(
decode_string(Enum_name, <<"enum_name"/utf8>>),
fun(Enum_name@1) ->
gleam@result:'try'(
decode_string(Enum_value, <<"enum_value"/utf8>>),
fun(Enum_value@1) ->
gleam@result:'try'(
decode_float(
Enum_sort_order,
<<"enum_sort_order"/utf8>>
),
fun(Enum_sort_order@1) ->
{ok,
{raw_enum_row,
Enum_name@1,
Enum_value@1,
Enum_sort_order@1}}
end
)
end
)
end
);
_ ->
{error, <<"Invalid enum row format"/utf8>>}
end.
-file("src/cquill/cli/generate.gleam", 473).
-spec decode_optional_string(gleam@dynamic:dynamic_()) -> gleam@option:option(binary()).
decode_optional_string(Value) ->
case gleam@dynamic@decode:run(
Value,
gleam@dynamic@decode:optional(
{decoder, fun gleam@dynamic@decode:decode_string/1}
)
) of
{ok, Opt} ->
Opt;
{error, _} ->
none
end.
-file("src/cquill/cli/generate.gleam", 480).
-spec decode_optional_int(gleam@dynamic:dynamic_()) -> gleam@option:option(integer()).
decode_optional_int(Value) ->
case gleam@dynamic@decode:run(
Value,
gleam@dynamic@decode:optional(
{decoder, fun gleam@dynamic@decode:decode_int/1}
)
) of
{ok, Opt} ->
Opt;
{error, _} ->
none
end.
-file("src/cquill/cli/generate.gleam", 292).
-spec parse_column_row(list(gleam@dynamic:dynamic_())) -> {ok,
cquill@introspection:raw_column_row()} |
{error, binary()}.
parse_column_row(Row) ->
case Row of
[Table_name,
Column_name,
Ordinal_position,
Data_type,
Udt_name,
Is_nullable,
Column_default,
Char_max_length,
Numeric_precision,
Numeric_scale] ->
gleam@result:'try'(
decode_string(Table_name, <<"table_name"/utf8>>),
fun(Table_name@1) ->
gleam@result:'try'(
decode_string(Column_name, <<"column_name"/utf8>>),
fun(Column_name@1) ->
gleam@result:'try'(
decode_int(
Ordinal_position,
<<"ordinal_position"/utf8>>
),
fun(Ordinal_position@1) ->
gleam@result:'try'(
decode_string(
Data_type,
<<"data_type"/utf8>>
),
fun(Data_type@1) ->
gleam@result:'try'(
decode_string(
Udt_name,
<<"udt_name"/utf8>>
),
fun(Udt_name@1) ->
gleam@result:'try'(
decode_string(
Is_nullable,
<<"is_nullable"/utf8>>
),
fun(Is_nullable@1) ->
Column_default@1 = decode_optional_string(
Column_default
),
Char_max_length@1 = decode_optional_int(
Char_max_length
),
Numeric_precision@1 = decode_optional_int(
Numeric_precision
),
Numeric_scale@1 = decode_optional_int(
Numeric_scale
),
{ok,
{raw_column_row,
Table_name@1,
Column_name@1,
Ordinal_position@1,
Data_type@1,
Udt_name@1,
Is_nullable@1,
Column_default@1,
Char_max_length@1,
Numeric_precision@1,
Numeric_scale@1}}
end
)
end
)
end
)
end
)
end
)
end
);
_ ->
{error, <<"Invalid column row format"/utf8>>}
end.
-file("src/cquill/cli/generate.gleam", 186).
?DOC(" Introspect the database schema\n").
-spec introspect_schema(
cquill@adapter@postgres:postgres_connection(),
cquill@cli@args:generate_options()
) -> {ok, cquill@introspection:introspected_schema()} | {error, binary()}.
introspect_schema(Conn, Options) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(
<<<<"Introspecting schema '"/utf8,
(erlang:element(4, Options))/binary>>/binary,
"'..."/utf8>>
);
false ->
nil
end,
Column_query = cquill@introspection:columns_query(),
Pk_query = cquill@introspection:primary_keys_query(),
Fk_query = cquill@introspection:foreign_keys_query(),
Unique_query = cquill@introspection:unique_constraints_query(),
Check_query = cquill@introspection:check_constraints_query(),
Enum_query = cquill@introspection:enums_query(),
gleam@result:'try'(
execute_introspection_query(
Conn,
Column_query,
[{param_string, erlang:element(4, Options)}],
fun parse_column_row/1,
<<"columns"/utf8>>
),
fun(Column_rows) ->
gleam@result:'try'(
execute_introspection_query(
Conn,
Pk_query,
[{param_string, erlang:element(4, Options)}],
fun parse_pk_row/1,
<<"primary keys"/utf8>>
),
fun(Pk_rows) ->
gleam@result:'try'(
execute_introspection_query(
Conn,
Fk_query,
[{param_string, erlang:element(4, Options)}],
fun parse_fk_row/1,
<<"foreign keys"/utf8>>
),
fun(Fk_rows) ->
gleam@result:'try'(
execute_introspection_query(
Conn,
Unique_query,
[{param_string, erlang:element(4, Options)}],
fun parse_unique_row/1,
<<"unique constraints"/utf8>>
),
fun(Unique_rows) ->
gleam@result:'try'(
execute_introspection_query(
Conn,
Check_query,
[{param_string,
erlang:element(4, Options)}],
fun parse_check_row/1,
<<"check constraints"/utf8>>
),
fun(Check_rows) ->
gleam@result:'try'(
execute_introspection_query(
Conn,
Enum_query,
[{param_string,
erlang:element(
4,
Options
)}],
fun parse_enum_row/1,
<<"enums"/utf8>>
),
fun(Enum_rows) ->
Schema = cquill@introspection:build_schema(
Column_rows,
Pk_rows,
Fk_rows,
Unique_rows,
Check_rows,
Enum_rows
),
{ok, Schema}
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/cquill/cli/generate.gleam", 492).
?DOC(" Filter tables based on include/exclude options\n").
-spec filter_tables(
cquill@introspection:introspected_schema(),
cquill@cli@args:generate_options()
) -> cquill@introspection:introspected_schema().
filter_tables(Schema, Options) ->
Include_tables = cquill@cli@args:parse_table_list(
erlang:element(5, Options)
),
Exclude_tables = cquill@cli@args:parse_table_list(
erlang:element(6, Options)
),
Filtered_tables = begin
_pipe = erlang:element(2, Schema),
gleam@list:filter(
_pipe,
fun(Table) ->
cquill@cli@args:should_include_table(
erlang:element(2, Table),
Include_tables,
Exclude_tables
)
end
)
end,
{introspected_schema, Filtered_tables, erlang:element(3, Schema)}.
-file("src/cquill/cli/generate.gleam", 513).
?DOC(" Generate code modules from the introspected schema\n").
-spec generate_code(
cquill@introspection:introspected_schema(),
cquill@cli@args:generate_options()
) -> {ok, list(cquill@codegen@generator:generated_module())} | {error, binary()}.
generate_code(Schema, Options) ->
Config = begin
_pipe = cquill@codegen@generator:default_config(),
_pipe@1 = cquill@codegen@generator:with_module_prefix(
_pipe,
erlang:element(7, Options)
),
cquill@codegen@generator:with_typed_columns(
_pipe@1,
erlang:element(13, Options)
)
end,
Modules = cquill@codegen@generator:generate_all(
erlang:element(2, Schema),
erlang:element(3, Schema),
Config
),
{ok, Modules}.
-file("src/cquill/cli/generate.gleam", 558).
?DOC(" Build the full file path for a generated module\n").
-spec build_file_path(
cquill@codegen@generator:generated_module(),
cquill@cli@args:generate_options()
) -> binary().
build_file_path(Module, Options) ->
Path_without_prefix = case gleam@string:split(
erlang:element(2, Module),
<<"/"/utf8>>
) of
[_ | Rest] ->
gleam@string:join(Rest, <<"/"/utf8>>);
_ ->
erlang:element(2, Module)
end,
<<<<<<(erlang:element(3, Options))/binary, "/"/utf8>>/binary,
Path_without_prefix/binary>>/binary,
".gleam"/utf8>>.
-file("src/cquill/cli/generate.gleam", 585).
?DOC(" Get the directory portion of a file path\n").
-spec get_directory(binary()) -> binary().
get_directory(Path) ->
case gleam@string:split(Path, <<"/"/utf8>>) of
[] ->
<<"."/utf8>>;
Parts ->
Dir_parts = gleam@list:take(Parts, erlang:length(Parts) - 1),
case Dir_parts of
[] ->
<<"."/utf8>>;
_ ->
gleam@string:join(Dir_parts, <<"/"/utf8>>)
end
end.
-file("src/cquill/cli/generate.gleam", 608).
?DOC(" Describe a simplifile error\n").
-spec describe_file_error(simplifile:file_error()) -> binary().
describe_file_error(Err) ->
case Err of
eacces ->
<<"Permission denied"/utf8>>;
enoent ->
<<"No such file or directory"/utf8>>;
eexist ->
<<"File already exists"/utf8>>;
enotdir ->
<<"Not a directory"/utf8>>;
enospc ->
<<"No space left on device"/utf8>>;
eio ->
<<"I/O error"/utf8>>;
_ ->
<<"Unknown error"/utf8>>
end.
-file("src/cquill/cli/generate.gleam", 573).
?DOC(" Ensure the directory for a file path exists\n").
-spec ensure_directory(binary()) -> {ok, nil} | {error, binary()}.
ensure_directory(File_path) ->
Dir = get_directory(File_path),
case simplifile:create_directory_all(Dir) of
{ok, _} ->
{ok, nil};
{error, Err} ->
{error,
<<<<<<"Failed to create directory "/utf8, Dir/binary>>/binary,
": "/utf8>>/binary,
(describe_file_error(Err))/binary>>}
end.
-file("src/cquill/cli/generate.gleam", 599).
?DOC(" Write content to a file\n").
-spec write_file(binary(), binary()) -> {ok, nil} | {error, binary()}.
write_file(Path, Content) ->
case simplifile:write(Path, Content) of
{ok, _} ->
{ok, nil};
{error, Err} ->
{error,
<<<<<<"Failed to write "/utf8, Path/binary>>/binary, ": "/utf8>>/binary,
(describe_file_error(Err))/binary>>}
end.
-file("src/cquill/cli/generate.gleam", 532).
?DOC(" Write generated modules to files\n").
-spec write_files(
list(cquill@codegen@generator:generated_module()),
cquill@cli@args:generate_options()
) -> {ok, integer()} | {error, binary()}.
write_files(Modules, Options) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(<<"\nGenerating:"/utf8>>);
false ->
nil
end,
_pipe = Modules,
gleam@list:try_fold(
_pipe,
0,
fun(Count, Module) ->
File_path = build_file_path(Module, Options),
gleam@result:'try'(
ensure_directory(File_path),
fun(_) ->
gleam@result:'try'(
write_file(File_path, erlang:element(4, Module)),
fun(_) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(
<<" ✓ "/utf8, File_path/binary>>
);
false ->
nil
end,
{ok, Count + 1}
end
)
end
)
end
).
-file("src/cquill/cli/generate.gleam", 625).
?DOC(" Show what would be generated without writing\n").
-spec show_dry_run(
list(cquill@codegen@generator:generated_module()),
cquill@cli@args:generate_options()
) -> nil.
show_dry_run(Modules, Options) ->
gleam_stdlib:println(<<"Dry run - would generate:\n"/utf8>>),
gleam@list:each(
Modules,
fun(Module) ->
File_path = build_file_path(Module, Options),
gleam_stdlib:println(<<" "/utf8, File_path/binary>>),
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(<<" Content preview:"/utf8>>),
Preview = begin
_pipe = erlang:element(4, Module),
_pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
_pipe@2 = gleam@list:take(_pipe@1, 5),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Line) -> <<" "/utf8, Line/binary>> end
),
gleam@string:join(_pipe@3, <<"\n"/utf8>>)
end,
gleam_stdlib:println(Preview),
gleam_stdlib:println(<<" ..."/utf8>>),
gleam_stdlib:println(<<""/utf8>>);
false ->
nil
end
end
),
gleam_stdlib:println(
<<<<"\nTotal: "/utf8,
(erlang:integer_to_binary(erlang:length(Modules)))/binary>>/binary,
" files"/utf8>>
).
-file("src/cquill/cli/generate.gleam", 660).
?DOC(" Run gleam format on the output directory\n").
-spec run_gleam_format(cquill@cli@args:generate_options()) -> nil.
run_gleam_format(Options) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(<<"\nRunning gleam format..."/utf8>>);
false ->
nil
end,
nil.
-file("src/cquill/cli/generate.gleam", 39).
?DOC(" Execute the generate command with the given options\n").
-spec run(cquill@cli@args:generate_options()) -> generate_result().
run(Options) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(<<"cquill generate v0.1.0\n"/utf8>>);
false ->
nil
end,
case connect_to_database(Options) of
{error, Err} ->
{generate_error, Err};
{ok, Conn} ->
case introspect_schema(Conn, Options) of
{error, Err@1} ->
{generate_error, Err@1};
{ok, Schema} ->
Filtered_schema = filter_tables(Schema, Options),
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(
<<<<<<<<"Found "/utf8,
(erlang:integer_to_binary(
erlang:length(
erlang:element(
2,
Filtered_schema
)
)
))/binary>>/binary,
" tables, "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:length(
erlang:element(
3,
Filtered_schema
)
)
))/binary>>/binary,
" enums\n"/utf8>>
);
false ->
nil
end,
case generate_code(Filtered_schema, Options) of
{error, Err@2} ->
{generate_error, Err@2};
{ok, Modules} ->
case erlang:element(11, Options) of
true ->
show_dry_run(Modules, Options),
{generate_success, erlang:length(Modules)};
false ->
case write_files(Modules, Options) of
{error, Err@3} ->
{generate_error, Err@3};
{ok, Count} ->
case erlang:element(10, Options) of
true ->
run_gleam_format(Options);
false ->
nil
end,
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(
<<<<"\nDone! Generated "/utf8,
(erlang:integer_to_binary(
Count
))/binary>>/binary,
" files."/utf8>>
);
false ->
nil
end,
{generate_success, Count}
end
end
end
end
end.
-file("src/cquill/cli/generate.gleam", 789).
?DOC(" Generate code from an already-introspected schema\n").
-spec generate_from_schema(
cquill@introspection:introspected_schema(),
cquill@cli@args:generate_options()
) -> {ok, integer()} | {error, binary()}.
generate_from_schema(Schema, Options) ->
case generate_code(Schema, Options) of
{error, Err} ->
{error, Err};
{ok, Modules} ->
case erlang:element(11, Options) of
true ->
show_dry_run(Modules, Options),
{ok, erlang:length(Modules)};
false ->
case write_files(Modules, Options) of
{error, Err@1} ->
{error, Err@1};
{ok, Count} ->
case erlang:element(10, Options) of
true ->
run_gleam_format(Options);
false ->
nil
end,
{ok, Count}
end
end
end.
-file("src/cquill/cli/generate.gleam", 887).
-spec hex_digit(integer()) -> binary().
hex_digit(N) ->
case N of
0 ->
<<"0"/utf8>>;
1 ->
<<"1"/utf8>>;
2 ->
<<"2"/utf8>>;
3 ->
<<"3"/utf8>>;
4 ->
<<"4"/utf8>>;
5 ->
<<"5"/utf8>>;
6 ->
<<"6"/utf8>>;
7 ->
<<"7"/utf8>>;
8 ->
<<"8"/utf8>>;
9 ->
<<"9"/utf8>>;
10 ->
<<"a"/utf8>>;
11 ->
<<"b"/utf8>>;
12 ->
<<"c"/utf8>>;
13 ->
<<"d"/utf8>>;
14 ->
<<"e"/utf8>>;
_ ->
<<"f"/utf8>>
end.
-file("src/cquill/cli/generate.gleam", 881).
-spec int_to_hex(integer()) -> binary().
int_to_hex(N) ->
High = N div 16,
Low = N rem 16,
<<(hex_digit(High))/binary, (hex_digit(Low))/binary>>.
-file("src/cquill/cli/generate.gleam", 871).
-spec bytes_to_hex_loop(bitstring(), binary()) -> binary().
bytes_to_hex_loop(Bytes, Acc) ->
case Bytes of
<<Byte/integer, Rest/bitstring>> ->
Hex = int_to_hex(Byte),
bytes_to_hex_loop(Rest, <<Acc/binary, Hex/binary>>);
_ ->
Acc
end.
-file("src/cquill/cli/generate.gleam", 867).
?DOC(" Convert bytes to hex string\n").
-spec bytes_to_hex(bitstring()) -> binary().
bytes_to_hex(Bytes) ->
bytes_to_hex_loop(Bytes, <<""/utf8>>).
-file("src/cquill/cli/generate.gleam", 819).
?DOC(" Generate a fingerprint of the schema for change detection\n").
-spec schema_fingerprint(cquill@introspection:introspected_schema()) -> binary().
schema_fingerprint(Schema) ->
Table_strings = begin
_pipe = erlang:element(2, Schema),
_pipe@1 = gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@string:compare(erlang:element(2, A), erlang:element(2, B))
end
),
_pipe@5 = gleam@list:map(
_pipe@1,
fun(Table) ->
Column_strings = begin
_pipe@2 = erlang:element(3, Table),
_pipe@3 = gleam@list:sort(
_pipe@2,
fun(A@1, B@1) ->
gleam@string:compare(
erlang:element(2, A@1),
erlang:element(2, B@1)
)
end
),
_pipe@4 = gleam@list:map(
_pipe@3,
fun(Col) ->
<<<<<<<<<<<<(erlang:element(2, Col))/binary,
":"/utf8>>/binary,
(erlang:element(4, Col))/binary>>/binary,
":"/utf8>>/binary,
(case erlang:element(6, Col) of
true ->
<<"null"/utf8>>;
false ->
<<"notnull"/utf8>>
end)/binary>>/binary,
":"/utf8>>/binary,
(case erlang:element(7, Col) of
{some, D} ->
D;
none ->
<<""/utf8>>
end)/binary>>
end
),
gleam@string:join(_pipe@4, <<","/utf8>>)
end,
<<<<<<(erlang:element(2, Table))/binary, "["/utf8>>/binary,
Column_strings/binary>>/binary,
"]"/utf8>>
end
),
gleam@string:join(_pipe@5, <<";"/utf8>>)
end,
Enum_strings = begin
_pipe@6 = erlang:element(3, Schema),
_pipe@7 = gleam@list:sort(
_pipe@6,
fun(A@2, B@2) ->
gleam@string:compare(
erlang:element(2, A@2),
erlang:element(2, B@2)
)
end
),
_pipe@8 = gleam@list:map(
_pipe@7,
fun(E) ->
<<<<(erlang:element(2, E))/binary, "="/utf8>>/binary,
(gleam@string:join(erlang:element(3, E), <<"|"/utf8>>))/binary>>
end
),
gleam@string:join(_pipe@8, <<";"/utf8>>)
end,
Combined = <<<<Table_strings/binary, "||"/utf8>>/binary,
Enum_strings/binary>>,
_pipe@9 = cquill_crypto_ffi:hash_sha256(Combined),
bytes_to_hex(_pipe@9).
-file("src/cquill/cli/generate.gleam", 770).
?DOC(" Introspect schema and generate code, returning fingerprint and file count\n").
-spec introspect_and_generate(
cquill@adapter@postgres:postgres_connection(),
cquill@cli@args:generate_options()
) -> {ok, {binary(), integer()}} | {error, binary()}.
introspect_and_generate(Conn, Options) ->
case introspect_schema(Conn, Options) of
{error, Err} ->
{error, Err};
{ok, Schema} ->
Filtered_schema = filter_tables(Schema, Options),
Fingerprint = schema_fingerprint(Filtered_schema),
case generate_from_schema(Filtered_schema, Options) of
{error, Err@1} ->
{error, Err@1};
{ok, Count} ->
{ok, {Fingerprint, Count}}
end
end.
-file("src/cquill/cli/generate.gleam", 909).
?DOC(" Print a summary of what changed in the schema\n").
-spec print_change_summary(
cquill@cli@args:generate_options(),
cquill@introspection:introspected_schema()
) -> nil.
print_change_summary(Options, Schema) ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(
<<" Tables: "/utf8,
(erlang:integer_to_binary(
erlang:length(erlang:element(2, Schema))
))/binary>>
),
gleam_stdlib:println(
<<" Enums: "/utf8,
(erlang:integer_to_binary(
erlang:length(erlang:element(3, Schema))
))/binary>>
);
false ->
nil
end.
-file("src/cquill/cli/generate.gleam", 939).
-spec pad_zero(integer()) -> binary().
pad_zero(N) ->
case N < 10 of
true ->
<<"0"/utf8, (erlang:integer_to_binary(N))/binary>>;
false ->
erlang:integer_to_binary(N)
end.
-file("src/cquill/cli/generate.gleam", 923).
?DOC(" Get current timestamp string for logging\n").
-spec timestamp() -> binary().
timestamp() ->
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(),
<<<<<<<<<<<<<<<<<<<<(erlang:integer_to_binary(Year))/binary, "-"/utf8>>/binary,
(pad_zero(Month))/binary>>/binary,
"-"/utf8>>/binary,
(pad_zero(Day))/binary>>/binary,
" "/utf8>>/binary,
(pad_zero(Hour))/binary>>/binary,
":"/utf8>>/binary,
(pad_zero(Minute))/binary>>/binary,
":"/utf8>>/binary,
(pad_zero(Second))/binary>>.
-file("src/cquill/cli/generate.gleam", 710).
?DOC(" Main watch loop that polls for schema changes\n").
-spec watch_loop(
cquill@cli@args:generate_options(),
cquill@adapter@postgres:postgres_connection(),
binary()
) -> nil.
watch_loop(Options, Conn, Last_fingerprint) ->
gleam_erlang_ffi:sleep(erlang:element(9, Options)),
case introspect_schema(Conn, Options) of
{error, Err} ->
gleam_stdlib:println_error(
<<"Warning: Failed to introspect schema: "/utf8, Err/binary>>
),
watch_loop(Options, Conn, Last_fingerprint);
{ok, Schema} ->
Filtered_schema = filter_tables(Schema, Options),
Current_fingerprint = schema_fingerprint(Filtered_schema),
case Current_fingerprint =:= Last_fingerprint of
true ->
case erlang:element(12, Options) of
true ->
gleam_stdlib:println(
<<<<"["/utf8, (timestamp())/binary>>/binary,
"] No changes detected"/utf8>>
);
false ->
nil
end,
watch_loop(Options, Conn, Last_fingerprint);
false ->
gleam_stdlib:println(
<<<<"\n["/utf8, (timestamp())/binary>>/binary,
"] Schema change detected, regenerating..."/utf8>>
),
case generate_from_schema(Filtered_schema, Options) of
{error, Err@1} ->
gleam_stdlib:println_error(
<<"Error regenerating: "/utf8, Err@1/binary>>
),
watch_loop(Options, Conn, Last_fingerprint);
{ok, Count} ->
print_change_summary(Options, Filtered_schema),
gleam_stdlib:println(
<<<<<<<<"["/utf8, (timestamp())/binary>>/binary,
"] Regenerated "/utf8>>/binary,
(erlang:integer_to_binary(Count))/binary>>/binary,
" files\n"/utf8>>
),
watch_loop(Options, Conn, Current_fingerprint)
end
end
end.
-file("src/cquill/cli/generate.gleam", 678).
?DOC(" Run in watch mode, regenerating on schema changes\n").
-spec watch(cquill@cli@args:generate_options()) -> nil.
watch(Options) ->
gleam_stdlib:println(<<"cquill watch mode starting...\n"/utf8>>),
case connect_to_database(Options) of
{error, Err} ->
gleam_stdlib:println_error(<<"Error: "/utf8, Err/binary>>);
{ok, Conn} ->
case introspect_and_generate(Conn, Options) of
{error, Err@1} ->
gleam_stdlib:println_error(
<<"Initial generation failed: "/utf8, Err@1/binary>>
);
{ok, {Fingerprint, Count}} ->
gleam_stdlib:println(
<<<<"Initial generation complete: "/utf8,
(erlang:integer_to_binary(Count))/binary>>/binary,
" files generated"/utf8>>
),
gleam_stdlib:println(
<<"\nWatching for schema changes..."/utf8>>
),
gleam_stdlib:println(
<<<<"Poll interval: "/utf8,
(erlang:integer_to_binary(
erlang:element(9, Options)
))/binary>>/binary,
"ms"/utf8>>
),
gleam_stdlib:println(<<"Press Ctrl+C to stop.\n"/utf8>>),
watch_loop(Options, Conn, Fingerprint)
end
end.