Current section

Files

Jump to
sqlode src sqlode@internal@config.erl
Raw

src/sqlode@internal@config.erl

-module(sqlode@internal@config).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/internal/config.gleam").
-export([error_to_string/1, load/1]).
-export_type([config_error/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.
?MODULEDOC(false).
-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/internal/config.gleam", 275).
?DOC(false).
-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@internal@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/internal/config.gleam", 306).
?DOC(false).
-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, <<Prefix/binary, Key/binary>>}
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/internal/config.gleam", 377).
?DOC(false).
-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/internal/config.gleam", 384).
?DOC(false).
-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/internal/config.gleam", 198).
?DOC(false).
-spec parse_overrides(yay:node_()) -> {ok, sqlode@internal@model:overrides()} |
{error, config_error()}.
parse_overrides(Node) ->
case yay:select_sugar(Node, <<"overrides"/utf8>>) of
{error, _} ->
{ok, sqlode@internal@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/internal/config.gleam", 393).
?DOC(false).
-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/internal/config.gleam", 405).
?DOC(false).
-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/internal/config.gleam", 56).
?DOC(false).
-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/internal/config.gleam", 338).
?DOC(false).
-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/internal/config.gleam", 350).
?DOC(false).
-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/internal/config.gleam", 97).
?DOC(false).
-spec parse_sql_block(yay:node_()) -> {ok, sqlode@internal@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@internal@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@internal@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@internal@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/internal/config.gleam", 80).
?DOC(false).
-spec parse_sql_blocks(yay:node_()) -> {ok,
list(sqlode@internal@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/internal/config.gleam", 415).
?DOC(false).
-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/internal/config.gleam", 432).
?DOC(false).
-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/internal/config.gleam", 20).
?DOC(false).
-spec load(binary()) -> {ok, sqlode@internal@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
).