-module(sqlode@cli). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sqlode/cli.gleam"). -export([decide_color_emission/2, app/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. -file("src/sqlode/cli.gleam", 53). ?DOC(false). -spec decide_color_emission(boolean(), {ok, binary()} | {error, nil}) -> boolean(). decide_color_emission(Is_terminal, No_color_env) -> No_color_active = case No_color_env of {ok, Value} -> Value /= <<""/utf8>>; {error, nil} -> false end, Is_terminal andalso not No_color_active. -file("src/sqlode/cli.gleam", 43). ?DOC( " Decide whether `--help` output should carry ANSI escape codes.\n" " Wraps the FFI probes for stdout's TTY status and the `NO_COLOR`\n" " environment variable so the policy itself is a small pure\n" " function that the test suite can pin without mocking the BEAM\n" " primitives. See `decide_color_emission` for the rule.\n" ). -spec should_emit_color() -> boolean(). should_emit_color() -> decide_color_emission( sqlode_ffi:is_stdout_terminal(), sqlode_ffi:no_color_env() ). -file("src/sqlode/cli.gleam", 208). -spec version_command() -> glint:command(nil). version_command() -> glint:command_help( <<"Print the sqlode version and exit."/utf8>>, fun() -> glint:command( fun(_, _, _) -> gleam_stdlib:println( <<"sqlode v"/utf8, (<<"0.15.0"/utf8>>)/binary>> ) end ) end ). -file("src/sqlode/cli.gleam", 320). -spec ensure_parent_directory(binary()) -> {ok, nil} | {error, binary()}. ensure_parent_directory(Dir) -> case Dir of <<""/utf8>> -> {ok, nil}; <<"."/utf8>> -> {ok, nil}; _ -> case simplifile:create_directory_all(Dir) of {ok, nil} -> {ok, nil}; {error, Err} -> {error, <<<<<<"failed to create directory "/utf8, Dir/binary>>/binary, ": "/utf8>>/binary, (simplifile:describe_error(Err))/binary>>} end end. -file("src/sqlode/cli.gleam", 337). -spec validate_init_flags(binary(), binary()) -> {ok, nil} | {error, binary()}. validate_init_flags(Engine, Runtime) -> _pipe = case Engine of <<"postgresql"/utf8>> -> {ok, nil}; <<"sqlite"/utf8>> -> {ok, nil}; <<"mysql"/utf8>> -> {ok, nil}; _ -> {error, <<<<"unsupported engine \""/utf8, Engine/binary>>/binary, "\"; expected postgresql, sqlite, or mysql"/utf8>>} end, gleam@result:'try'(_pipe, fun(_) -> case Runtime of <<"raw"/utf8>> -> {ok, nil}; <<"native"/utf8>> -> {ok, nil}; _ -> {error, <<<<"unsupported runtime \""/utf8, Runtime/binary>>/binary, "\"; expected raw or native"/utf8>>} end end). -file("src/sqlode/cli.gleam", 358). -spec config_template(binary(), binary()) -> binary(). config_template(Engine, Runtime) -> <<<<<<<<<<<<<<<<"version: \"2\" sql: - schema: \"db/schema.sql\" queries: \"db/query.sql\" engine: \""/utf8, Engine/binary>>/binary, "\"\n"/utf8>>/binary, " gen:\n"/utf8>>/binary, " gleam:\n"/utf8>>/binary, " out: \"src/db\"\n"/utf8>>/binary, " runtime: \""/utf8>>/binary, Runtime/binary>>/binary, "\"\n"/utf8>>. -file("src/sqlode/cli.gleam", 395). -spec starter_schema(binary()) -> binary(). starter_schema(Engine) -> case Engine of <<"sqlite"/utf8>> -> <<"CREATE TABLE authors ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, bio TEXT, created_at TEXT NOT NULL ); "/utf8>>; <<"mysql"/utf8>> -> <<"CREATE TABLE authors ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name TEXT NOT NULL, bio TEXT, created_at DATETIME NOT NULL ); "/utf8>>; _ -> <<"CREATE TABLE authors ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, bio TEXT, created_at TIMESTAMP NOT NULL ); "/utf8>> end. -file("src/sqlode/cli.gleam", 424). -spec starter_query(binary()) -> binary(). starter_query(Engine) -> Get_placeholder = case Engine of <<"mysql"/utf8>> -> <<"?"/utf8>>; <<"sqlite"/utf8>> -> <<"?"/utf8>>; _ -> <<"$1"/utf8>> end, <<<<<<<<<<<<<<<<<<<<<<"-- name: GetAuthor :one SELECT id, name, bio FROM authors WHERE id = "/utf8, Get_placeholder/binary>>/binary, ";\n"/utf8>>/binary, "\n"/utf8>>/binary, "-- name: ListAuthors :many\n"/utf8>>/binary, "SELECT id, name\n"/utf8>>/binary, "FROM authors\n"/utf8>>/binary, "ORDER BY name;\n"/utf8>>/binary, "\n"/utf8>>/binary, "-- name: CreateAuthor :exec\n"/utf8>>/binary, "INSERT INTO authors (name, bio)\n"/utf8>>/binary, "VALUES (sqlode.arg(author_name), sqlode.narg(bio));\n"/utf8>>. -file("src/sqlode/cli.gleam", 366). -spec create_stub_files(binary(), binary()) -> nil. create_stub_files(Base_dir, Engine) -> Schema_content = starter_schema(Engine), Query_content = starter_query(Engine), Db_dir = filepath:join(Base_dir, <<"db"/utf8>>), Schema_path = filepath:join(Db_dir, <<"schema.sql"/utf8>>), Query_path = filepath:join(Db_dir, <<"query.sql"/utf8>>), _ = simplifile:create_directory_all(Db_dir), case simplifile_erl:is_file(Schema_path) of {ok, true} -> gleam_stdlib:println( <<<<" Skipped "/utf8, Schema_path/binary>>/binary, " (already exists)"/utf8>> ); _ -> case simplifile:write(Schema_path, Schema_content) of {ok, _} -> gleam_stdlib:println( <<" Created "/utf8, Schema_path/binary>> ); {error, _} -> gleam_stdlib:println( <<" Warning: failed to create "/utf8, Schema_path/binary>> ) end end, case simplifile_erl:is_file(Query_path) of {ok, true} -> gleam_stdlib:println( <<<<" Skipped "/utf8, Query_path/binary>>/binary, " (already exists)"/utf8>> ); _ -> case simplifile:write(Query_path, Query_content) of {ok, _} -> gleam_stdlib:println( <<" Created "/utf8, Query_path/binary>> ); {error, _} -> gleam_stdlib:println( <<" Warning: failed to create "/utf8, Query_path/binary>> ) end end. -file("src/sqlode/cli.gleam", 277). -spec run_init(binary(), binary(), binary()) -> nil. run_init(Path, Engine, Runtime) -> case validate_init_flags(Engine, Runtime) of {error, Msg} -> gleam_stdlib:println_error(<<"Error: "/utf8, Msg/binary>>), init:stop(1); {ok, nil} -> Template = config_template(Engine, Runtime), case simplifile_erl:is_file(Path) of {ok, true} -> gleam_stdlib:println_error( <<<<"Error: "/utf8, Path/binary>>/binary, " already exists"/utf8>> ), init:stop(1); _ -> Parent_dir = filepath:directory_name(Path), case ensure_parent_directory(Parent_dir) of {error, Msg@1} -> gleam_stdlib:println_error( <<"Error: "/utf8, Msg@1/binary>> ), init:stop(1); {ok, nil} -> case simplifile:write(Path, Template) of {ok, _} -> gleam_stdlib:println( <<"Created "/utf8, Path/binary>> ), create_stub_files(Parent_dir, Engine); {error, Err} -> gleam_stdlib:println_error( <<<<<<"Error: failed to write "/utf8, Path/binary>>/binary, ": "/utf8>>/binary, (simplifile:describe_error(Err))/binary>> ), init:stop(1) end end end end. -file("src/sqlode/cli.gleam", 107). -spec init_command() -> glint:command(nil). init_command() -> begin glint:flag( begin _pipe = glint:string_flag(<<"output"/utf8>>), _pipe@1 = glint:flag_default(_pipe, <<"./sqlode.yaml"/utf8>>), glint:flag_help( _pipe@1, <<"Output path for generated config (default: ./sqlode.yaml)"/utf8>> ) end, fun(Output_path) -> glint:flag( begin _pipe@2 = glint:string_flag(<<"engine"/utf8>>), _pipe@3 = glint:flag_default( _pipe@2, <<"postgresql"/utf8>> ), glint:flag_help( _pipe@3, <<"Target engine: postgresql | sqlite | mysql (default: postgresql)"/utf8>> ) end, fun(Engine_flag) -> glint:flag( begin _pipe@4 = glint:string_flag(<<"runtime"/utf8>>), _pipe@5 = glint:flag_default( _pipe@4, <<"raw"/utf8>> ), glint:flag_help( _pipe@5, <<"Generated runtime: raw | native (default: raw)"/utf8>> ) end, fun(Runtime_flag) -> glint:command_help( <<"Scaffold a sqlode.yaml plus starter db/schema.sql and db/query.sql. Examples: sqlode init sqlode init --output=./config/sqlode.yaml sqlode init --engine=sqlite --runtime=native sqlode init --engine=mysql"/utf8>>, fun() -> glint:command( fun(_, _, Flags) -> Path = begin _pipe@6 = Output_path(Flags), gleam@result:unwrap( _pipe@6, <<"./sqlode.yaml"/utf8>> ) end, Engine = begin _pipe@7 = Engine_flag(Flags), gleam@result:unwrap( _pipe@7, <<"postgresql"/utf8>> ) end, Runtime = begin _pipe@8 = Runtime_flag( Flags ), gleam@result:unwrap( _pipe@8, <<"raw"/utf8>> ) end, run_init(Path, Engine, Runtime) end ) end ) end ) end ) end ) end. -file("src/sqlode/cli.gleam", 251). -spec autodiscover_config() -> {ok, binary()} | {error, binary()}. autodiscover_config() -> Found = gleam@list:filter( [<<"sqlode.yaml"/utf8>>, <<"sqlode.yml"/utf8>>, <<"sqlc.yaml"/utf8>>, <<"sqlc.yml"/utf8>>, <<"sqlc.json"/utf8>>], fun(Path) -> case simplifile_erl:is_file(Path) of {ok, true} -> true; _ -> false end end ), case Found of [] -> {error, <<<<"No config file found. Looked for: "/utf8, (gleam@string:join( [<<"sqlode.yaml"/utf8>>, <<"sqlode.yml"/utf8>>, <<"sqlc.yaml"/utf8>>, <<"sqlc.yml"/utf8>>, <<"sqlc.json"/utf8>>], <<", "/utf8>> ))/binary>>/binary, ". Create one with `sqlode init` or pass --config=."/utf8>>}; [Single] -> {ok, Single}; Multiple -> {error, <<<<"Multiple config files found: "/utf8, (gleam@string:join(Multiple, <<", "/utf8>>))/binary>>/binary, ". Pick one explicitly with --config=."/utf8>>} end. -file("src/sqlode/cli.gleam", 244). -spec resolve_config_path(binary()) -> {ok, binary()} | {error, binary()}. resolve_config_path(Flag_value) -> case Flag_value of <<""/utf8>> -> autodiscover_config(); Explicit -> {ok, Explicit} end. -file("src/sqlode/cli.gleam", 183). -spec run_verify(binary()) -> nil. run_verify(Flag_value) -> case resolve_config_path(Flag_value) of {error, Message} -> gleam_stdlib:println_error(<<"Error: "/utf8, Message/binary>>), init:stop(1); {ok, Config_path} -> gleam_stdlib:println( <<"Verifying config: "/utf8, Config_path/binary>> ), case sqlode@verify:run(Config_path) of {error, Err} -> gleam_stdlib:println_error( <<"Error: "/utf8, (sqlode@verify:error_to_string(Err))/binary>> ), init:stop(1); {ok, Report} -> gleam_stdlib:println(sqlode@verify:report_to_string(Report)), case erlang:element(2, Report) of [] -> nil; _ -> init:stop(1) end end end. -file("src/sqlode/cli.gleam", 149). -spec verify_command() -> glint:command(nil). verify_command() -> begin glint:flag( begin _pipe = glint:string_flag(<<"config"/utf8>>), _pipe@1 = glint:flag_default(_pipe, <<""/utf8>>), glint:flag_help( _pipe@1, <<"Path to config file. Default: auto-discover sqlode.yaml, sqlode.yml, sqlc.yaml, sqlc.yml, or sqlc.json in the current directory."/utf8>> ) end, fun(Config_path) -> glint:command_help( <<"Run static verification on the project without writing files. Loads the config, parses every schema and query the generator would use, and runs the full analyser pass. Any query-analysis error, schema warning under strict_views, or policy violation (for example `query_parameter_limit`) is collected into a single report. The command exits non-zero when at least one finding is reported — suitable for CI gates before the generation step runs. Examples: sqlode verify sqlode verify --config=./custom.yaml"/utf8>>, fun() -> glint:command( fun(_, _, Flags) -> Flag_value = begin _pipe@2 = Config_path(Flags), gleam@result:unwrap(_pipe@2, <<""/utf8>>) end, run_verify(Flag_value) end ) end ) end ) end. -file("src/sqlode/cli.gleam", 216). -spec run_generate(binary()) -> nil. run_generate(Flag_value) -> case resolve_config_path(Flag_value) of {error, Message} -> gleam_stdlib:println_error(<<"Error: "/utf8, Message/binary>>), init:stop(1); {ok, Config_path} -> gleam_stdlib:println( <<"Loading config from: "/utf8, Config_path/binary>> ), case sqlode@generate:run(Config_path) of {ok, Written} -> gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println( <<<<"Successfully generated "/utf8, (erlang:integer_to_binary( erlang:length(Written) ))/binary>>/binary, " files"/utf8>> ), gleam@list:each( Written, fun(Path) -> gleam_stdlib:println( <<" Generated: "/utf8, Path/binary>> ) end ); {error, Error} -> gleam_stdlib:println_error( <<"Error: "/utf8, (sqlode@generate:error_to_string(Error))/binary>> ), init:stop(1) end end. -file("src/sqlode/cli.gleam", 77). -spec generate_command() -> glint:command(nil). generate_command() -> begin glint:flag( begin _pipe = glint:string_flag(<<"config"/utf8>>), _pipe@1 = glint:flag_default(_pipe, <<""/utf8>>), glint:flag_help( _pipe@1, <<"Path to config file. Default: auto-discover sqlode.yaml, sqlode.yml, sqlc.yaml, sqlc.yml, or sqlc.json in the current directory."/utf8>> ) end, fun(Config_path) -> glint:command_help( <<"Parse SQL schema and queries, emit Gleam code. Without --config, generate auto-discovers sqlode.yaml, sqlode.yml, sqlc.yaml, sqlc.yml, or sqlc.json in the current directory and fails if more than one candidate exists. Examples: sqlode generate sqlode generate --config=./custom.yaml"/utf8>>, fun() -> glint:command( fun(_, _, Flags) -> Flag_value = begin _pipe@2 = Config_path(Flags), gleam@result:unwrap(_pipe@2, <<""/utf8>>) end, run_generate(Flag_value) end ) end ) end ) end. -file("src/sqlode/cli.gleam", 13). -spec app() -> glint:glint(nil). app() -> Base = begin _pipe = glint:new(), glint:with_name(_pipe, <<"sqlode"/utf8>>) end, With_styling = case should_emit_color() of true -> _pipe@1 = Base, glint:pretty_help(_pipe@1, glint:default_pretty_help()); false -> Base end, _pipe@2 = With_styling, _pipe@3 = glint:add(_pipe@2, [<<"generate"/utf8>>], generate_command()), _pipe@4 = glint:add(_pipe@3, [<<"init"/utf8>>], init_command()), _pipe@5 = glint:add(_pipe@4, [<<"verify"/utf8>>], verify_command()), glint:add(_pipe@5, [<<"version"/utf8>>], version_command()).