-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, _] -> <<<<<<<>/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, NSZ} | {error, binary()}), binary() ) -> {ok, list(NSZ)} | {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 <> -> Hex = int_to_hex(Byte), bytes_to_hex_loop(Rest, <>); _ -> 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 = <<<>/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.