-module(sqlode@config). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sqlode/config.gleam"). -export([error_to_string/1, load/1]). -export_type([config_error/0]). -type config_error() :: {file_not_found, binary()} | {file_read_error, binary(), binary()} | {parse_error, binary()} | {missing_field, binary()} | {invalid_value, binary(), binary()} | {unsupported_fields, list(binary()), binary()}. -file("src/sqlode/config.gleam", 275). -spec validate_gleam_type(binary()) -> {ok, nil} | {error, config_error()}. validate_gleam_type(Gleam_type) -> case gleam@string:is_empty(Gleam_type) of true -> {error, {invalid_value, <<"overrides.types.gleam_type"/utf8>>, <<"must not be empty"/utf8>>}}; false -> Type_name = case gleam@string:split_once(Gleam_type, <<"."/utf8>>) of {ok, {_, Name}} -> Name; {error, _} -> Gleam_type end, First = begin _pipe = gleam@string:first(Type_name), gleam@result:unwrap(_pipe, <<""/utf8>>) end, case sqlode@char_utils:is_uppercase_letter(First) of false -> {error, {invalid_value, <<"overrides.types.gleam_type"/utf8>>, <<<<"type name must start with an uppercase letter, got \""/utf8, Gleam_type/binary>>/binary, "\""/utf8>>}}; true -> {ok, nil} end end. -file("src/sqlode/config.gleam", 306). -spec check_unknown_keys(yay:node_(), list(binary()), binary()) -> {ok, nil} | {error, config_error()}. check_unknown_keys(Node, Known_keys, Prefix) -> case Node of {node_map, Pairs} -> Unknown = gleam@list:filter_map( Pairs, fun(Pair) -> case erlang:element(1, Pair) of {node_str, Key} -> case gleam@list:contains(Known_keys, Key) of true -> {error, nil}; false -> {ok, <>} end; _ -> {error, nil} end end ), case Unknown of [] -> {ok, nil}; Fields -> {error, {unsupported_fields, Fields, <<"these sqlc options are not supported by sqlode; please remove them. Valid keys: "/utf8, (gleam@string:join(Known_keys, <<", "/utf8>>))/binary>>}} end; _ -> {ok, nil} end. -file("src/sqlode/config.gleam", 377). -spec optional_string(yay:node_(), binary()) -> gleam@option:option(binary()). optional_string(Node, Selector) -> case yay:select_sugar(Node, Selector) of {ok, {node_str, Text}} -> {some, Text}; _ -> none end. -file("src/sqlode/config.gleam", 384). -spec optional_bool(yay:node_(), binary()) -> gleam@option:option(boolean()). optional_bool(Node, Selector) -> case yay:select_sugar(Node, Selector) of {ok, {node_str, <<"true"/utf8>>}} -> {some, true}; {ok, {node_str, <<"false"/utf8>>}} -> {some, false}; {ok, {node_bool, Value}} -> {some, Value}; _ -> none end. -file("src/sqlode/config.gleam", 198). -spec parse_overrides(yay:node_()) -> {ok, sqlode@model:overrides()} | {error, config_error()}. parse_overrides(Node) -> case yay:select_sugar(Node, <<"overrides"/utf8>>) of {error, _} -> {ok, sqlode@model:empty_overrides()}; {ok, Overrides_node} -> gleam@result:'try'( case yay:select_sugar(Overrides_node, <<"types"/utf8>>) of {ok, {node_seq, Items}} -> gleam@list:try_map( Items, fun(Item) -> Gleam_type = optional_string( Item, <<"gleam_type"/utf8>> ), Db_type = optional_string( Item, <<"db_type"/utf8>> ), Column = optional_string( Item, <<"column"/utf8>> ), Nullable = optional_bool( Item, <<"nullable"/utf8>> ), case {Column, Db_type, Gleam_type} of {{some, Col}, _, {some, Gt}} -> gleam@result:'try'( validate_gleam_type(Gt), fun(_) -> case gleam@string:split( Col, <<"."/utf8>> ) of [Table, Col_name] -> {ok, {column_override, Table, Col_name, Gt}}; _ -> {error, {invalid_value, <<"overrides.types.column"/utf8>>, <<<<"must be in \"table.column\" format, got \""/utf8, Col/binary>>/binary, "\""/utf8>>}} end end ); {_, {some, Dt}, {some, Gt@1}} -> gleam@result:'try'( validate_gleam_type(Gt@1), fun(_) -> {ok, {db_type_override, Dt, Gt@1, Nullable}} end ); {_, _, _} -> {error, {invalid_value, <<"overrides.types"/utf8>>, <<"each entry must have \"gleam_type\" and either \"db_type\" or \"column\""/utf8>>}} end end ); _ -> {ok, []} end, fun(Type_overrides) -> gleam@result:'try'( case yay:select_sugar( Overrides_node, <<"renames"/utf8>> ) of {ok, {node_seq, Items@1}} -> gleam@list:try_map( Items@1, fun(Item@1) -> case {optional_string( Item@1, <<"table"/utf8>> ), optional_string( Item@1, <<"column"/utf8>> ), optional_string( Item@1, <<"rename_to"/utf8>> )} of {{some, Table@1}, {some, Column@1}, {some, Rename_to}} -> {ok, {column_rename, Table@1, Column@1, Rename_to}}; {_, _, _} -> {error, {invalid_value, <<"overrides.renames"/utf8>>, <<"each rename entry must have 'table', 'column', and 'rename_to' fields"/utf8>>}} end end ); _ -> {ok, []} end, fun(Column_renames) -> {ok, {overrides, Type_overrides, Column_renames}} end ) end ) end. -file("src/sqlode/config.gleam", 393). -spec optional_int(yay:node_(), binary()) -> gleam@option:option(integer()). optional_int(Node, Selector) -> case yay:select_sugar(Node, Selector) of {ok, {node_str, Text}} -> case gleam_stdlib:parse_int(Text) of {ok, N} -> {some, N}; {error, _} -> none end; {ok, {node_int, Value}} -> {some, Value}; _ -> none end. -file("src/sqlode/config.gleam", 405). -spec require_node(yay:node_(), binary()) -> {ok, yay:node_()} | {error, config_error()}. require_node(Node, Selector) -> case yay:select_sugar(Node, Selector) of {ok, Child} -> {ok, Child}; {error, _} -> {error, {missing_field, Selector}} end. -file("src/sqlode/config.gleam", 56). -spec parse_version(yay:node_()) -> {ok, integer()} | {error, config_error()}. parse_version(Root) -> gleam@result:'try'( require_node(Root, <<"version"/utf8>>), fun(Node) -> case Node of {node_str, <<"2"/utf8>>} -> {ok, 2}; {node_int, 2} -> {ok, 2}; {node_str, Other} -> {error, {invalid_value, <<"version"/utf8>>, <<"expected \"2\", got "/utf8, Other/binary>>}}; {node_int, Other@1} -> {error, {invalid_value, <<"version"/utf8>>, <<"expected 2, got "/utf8, (erlang:integer_to_binary(Other@1))/binary>>}}; _ -> {error, {invalid_value, <<"version"/utf8>>, <<"must be a string or integer"/utf8>>}} end end ). -file("src/sqlode/config.gleam", 338). -spec required_string(yay:node_(), binary()) -> {ok, binary()} | {error, config_error()}. required_string(Node, Selector) -> gleam@result:'try'(require_node(Node, Selector), fun(Value) -> case Value of {node_str, Text} -> {ok, Text}; _ -> {error, {invalid_value, Selector, <<"must be a string"/utf8>>}} end end). -file("src/sqlode/config.gleam", 350). -spec required_string_list(yay:node_(), binary()) -> {ok, list(binary())} | {error, config_error()}. required_string_list(Node, Selector) -> gleam@result:'try'(require_node(Node, Selector), fun(Value) -> case Value of {node_str, Text} -> {ok, [Text]}; {node_seq, Items} -> gleam@list:try_map(Items, fun(Item) -> case Item of {node_str, Text@1} -> {ok, Text@1}; _ -> {error, {invalid_value, Selector, <<"must contain only strings"/utf8>>}} end end); _ -> {error, {invalid_value, Selector, <<"must be a string or a list of strings"/utf8>>}} end end). -file("src/sqlode/config.gleam", 97). -spec parse_sql_block(yay:node_()) -> {ok, sqlode@model:sql_block()} | {error, config_error()}. parse_sql_block(Node) -> gleam@result:'try'( check_unknown_keys( Node, [<<"name"/utf8>>, <<"engine"/utf8>>, <<"schema"/utf8>>, <<"queries"/utf8>>, <<"gen"/utf8>>, <<"overrides"/utf8>>], <<"sql."/utf8>> ), fun(_) -> Name = optional_string(Node, <<"name"/utf8>>), gleam@result:'try'( required_string(Node, <<"engine"/utf8>>), fun(Engine_text) -> gleam@result:'try'( begin _pipe = sqlode@model:parse_engine(Engine_text), gleam@result:map_error( _pipe, fun(Detail) -> {invalid_value, <<"sql.engine"/utf8>>, Detail} end ) end, fun(Engine) -> gleam@result:'try'( required_string_list(Node, <<"schema"/utf8>>), fun(Schema) -> gleam@result:'try'( required_string_list( Node, <<"queries"/utf8>> ), fun(Queries) -> gleam@result:'try'( require_node( Node, <<"gen"/utf8>> ), fun(Gen_node) -> gleam@result:'try'( check_unknown_keys( Gen_node, [<<"gleam"/utf8>>], <<"sql.gen."/utf8>> ), fun(_) -> gleam@result:'try'( require_node( Gen_node, <<"gleam"/utf8>> ), fun(Gleam_node) -> gleam@result:'try'( check_unknown_keys( Gleam_node, [<<"out"/utf8>>, <<"runtime"/utf8>>, <<"type_mapping"/utf8>>, <<"emit_sql_as_comment"/utf8>>, <<"emit_exact_table_names"/utf8>>, <<"omit_unused_models"/utf8>>, <<"vendor_runtime"/utf8>>, <<"strict_views"/utf8>>, <<"query_parameter_limit"/utf8>>], <<"sql.gen.gleam."/utf8>> ), fun(_) -> gleam@result:'try'( required_string( Gleam_node, <<"out"/utf8>> ), fun( Out ) -> gleam@result:'try'( case optional_string( Gleam_node, <<"runtime"/utf8>> ) of {some, Value} -> _pipe@1 = sqlode@model:parse_runtime( Value ), gleam@result:map_error( _pipe@1, fun( Detail@1 ) -> {invalid_value, <<"sql.gen.gleam.runtime"/utf8>>, Detail@1} end ); none -> {ok, raw} end, fun( Runtime ) -> gleam@result:'try'( case optional_string( Gleam_node, <<"type_mapping"/utf8>> ) of {some, Value@1} -> _pipe@2 = sqlode@model:parse_type_mapping( Value@1 ), gleam@result:map_error( _pipe@2, fun( Detail@2 ) -> {invalid_value, <<"sql.gen.gleam.type_mapping"/utf8>>, Detail@2} end ); none -> {ok, string_mapping} end, fun( Type_mapping ) -> Emit_sql_as_comment = begin _pipe@3 = optional_bool( Gleam_node, <<"emit_sql_as_comment"/utf8>> ), gleam@option:unwrap( _pipe@3, false ) end, Emit_exact_table_names = begin _pipe@4 = optional_bool( Gleam_node, <<"emit_exact_table_names"/utf8>> ), gleam@option:unwrap( _pipe@4, false ) end, Omit_unused_models = begin _pipe@5 = optional_bool( Gleam_node, <<"omit_unused_models"/utf8>> ), gleam@option:unwrap( _pipe@5, false ) end, Vendor_runtime = begin _pipe@6 = optional_bool( Gleam_node, <<"vendor_runtime"/utf8>> ), gleam@option:unwrap( _pipe@6, false ) end, Strict_views = begin _pipe@7 = optional_bool( Gleam_node, <<"strict_views"/utf8>> ), gleam@option:unwrap( _pipe@7, true ) end, Query_parameter_limit = optional_int( Gleam_node, <<"query_parameter_limit"/utf8>> ), gleam@result:'try'( parse_overrides( Node ), fun( Overrides ) -> {ok, {sql_block, Name, Engine, Schema, Queries, {gleam_output, Out, Runtime, Type_mapping, Emit_sql_as_comment, Emit_exact_table_names, Omit_unused_models, Vendor_runtime, Strict_views, Query_parameter_limit}, Overrides}} end ) end ) end ) end ) end ) end ) end ) end ) end ) end ) end ) end ) end ). -file("src/sqlode/config.gleam", 80). -spec parse_sql_blocks(yay:node_()) -> {ok, list(sqlode@model:sql_block())} | {error, config_error()}. parse_sql_blocks(Root) -> gleam@result:'try'( require_node(Root, <<"sql"/utf8>>), fun(Node) -> case Node of {node_seq, Items} -> case Items of [] -> {error, {invalid_value, <<"sql"/utf8>>, <<"must contain at least one entry"/utf8>>}}; _ -> gleam@list:try_map(Items, fun parse_sql_block/1) end; _ -> {error, {invalid_value, <<"sql"/utf8>>, <<"must be a list"/utf8>>}} end end ). -file("src/sqlode/config.gleam", 415). -spec error_to_string(config_error()) -> binary(). error_to_string(Error) -> case Error of {file_not_found, Path} -> <<"Config file not found: "/utf8, Path/binary>>; {file_read_error, Path@1, Detail} -> <<<<<<"Error reading config file "/utf8, Path@1/binary>>/binary, ": "/utf8>>/binary, Detail/binary>>; {parse_error, Detail@1} -> <<"Config parse error: "/utf8, Detail@1/binary>>; {missing_field, Field} -> <<"Missing required config field: "/utf8, Field/binary>>; {invalid_value, Field@1, Detail@2} -> <<<<<<"Invalid value for "/utf8, Field@1/binary>>/binary, ": "/utf8>>/binary, Detail@2/binary>>; {unsupported_fields, Fields, Message} -> <<<<<<"Unsupported config fields: "/utf8, (gleam@string:join(Fields, <<", "/utf8>>))/binary>>/binary, " — "/utf8>>/binary, Message/binary>> end. -file("src/sqlode/config.gleam", 432). -spec yaml_error_to_string(yay:yaml_error()) -> binary(). yaml_error_to_string(Error) -> case Error of unexpected_parsing_error -> <<"Unexpected parsing error"/utf8>>; {parsing_error, Msg, _} -> Msg end. -file("src/sqlode/config.gleam", 20). -spec load(binary()) -> {ok, sqlode@model:config()} | {error, config_error()}. load(Path) -> gleam@result:'try'( begin _pipe = simplifile:read(Path), gleam@result:map_error(_pipe, fun(Error) -> case Error of enoent -> {file_not_found, Path}; _ -> {file_read_error, Path, <<"Failed to read file: "/utf8, (simplifile:describe_error(Error))/binary>>} end end) end, fun(Content) -> gleam@result:'try'( begin _pipe@1 = yaml_ffi:parse_string(Content), gleam@result:map_error( _pipe@1, fun(Error@1) -> {parse_error, <<"YAML parse error: "/utf8, (yaml_error_to_string(Error@1))/binary>>} end ) end, fun(Docs) -> gleam@result:'try'(case Docs of [First | _] -> {ok, First}; [] -> {error, {parse_error, <<"Empty YAML document"/utf8>>}} end, fun(Doc) -> Root = yay:document_root(Doc), gleam@result:'try'( check_unknown_keys( Root, [<<"version"/utf8>>, <<"sql"/utf8>>], <<""/utf8>> ), fun(_) -> gleam@result:'try'( parse_version(Root), fun(Version) -> gleam@result:'try'( parse_sql_blocks(Root), fun(Sql) -> {ok, {config, Version, Sql}} end ) end ) end ) end) end ) end ).