Packages

A compile-time safe database library for Gleam - Ecto-inspired schemas, composable queries, and adapter-based persistence

Current section

Files

Jump to
cquill src cquill@codegen@type_mapping.erl
Raw

src/cquill@codegen@type_mapping.erl

-module(cquill@codegen@type_mapping).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/cquill/codegen/type_mapping.gleam").
-export([render_type/1, render_qualified_type/1, get_imports/1, collect_imports/1, pascal_case/1, map_postgres_type/4, map_column/6, snake_case/1, is_nullable_type/1, is_list_type/1, is_custom_type/1, unwrap_option/1, unwrap_list/1, types_equal/2]).
-export_type([gleam_type/0, column_meta/0, type_mapping_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.
-type gleam_type() :: gleam_int |
gleam_float |
gleam_string |
gleam_bool |
gleam_bit_array |
gleam_time |
gleam_date |
gleam_time_of_day |
gleam_decimal |
gleam_uuid |
gleam_json |
{gleam_list, gleam_type()} |
{gleam_option, gleam_type()} |
{gleam_custom, binary(), binary()}.
-type column_meta() :: {column_meta,
gleam_type(),
boolean(),
binary(),
binary(),
binary(),
boolean()}.
-type type_mapping_error() :: {unknown_type, binary(), binary()} |
{unsupported_array, binary()}.
-file("src/cquill/codegen/type_mapping.gleam", 403).
?DOC(" Detect if a column is auto-generated based on type and default\n").
-spec detect_auto_generated(binary(), gleam@option:option(binary())) -> boolean().
detect_auto_generated(Data_type, Column_default) ->
Lowercase_type = string:lowercase(Data_type),
Is_serial = case Lowercase_type of
<<"serial"/utf8>> ->
true;
<<"serial2"/utf8>> ->
true;
<<"serial4"/utf8>> ->
true;
<<"smallserial"/utf8>> ->
true;
<<"bigserial"/utf8>> ->
true;
<<"serial8"/utf8>> ->
true;
_ ->
false
end,
Has_sequence_default = case Column_default of
{some, Default_expr} ->
gleam_stdlib:contains_string(
string:lowercase(Default_expr),
<<"nextval"/utf8>>
);
none ->
false
end,
Has_auto_default = case Column_default of
{some, Default_expr@1} ->
Lower = string:lowercase(Default_expr@1),
(((gleam_stdlib:contains_string(Lower, <<"now()"/utf8>>) orelse gleam_stdlib:contains_string(
Lower,
<<"current_timestamp"/utf8>>
))
orelse gleam_stdlib:contains_string(Lower, <<"current_date"/utf8>>))
orelse gleam_stdlib:contains_string(
Lower,
<<"gen_random_uuid"/utf8>>
))
orelse gleam_stdlib:contains_string(Lower, <<"uuid_generate"/utf8>>);
none ->
false
end,
(Is_serial orelse Has_sequence_default) orelse Has_auto_default.
-file("src/cquill/codegen/type_mapping.gleam", 444).
?DOC(" Render a GleamType as a Gleam type string for code generation\n").
-spec render_type(gleam_type()) -> binary().
render_type(Gleam_type) ->
case Gleam_type of
gleam_int ->
<<"Int"/utf8>>;
gleam_float ->
<<"Float"/utf8>>;
gleam_string ->
<<"String"/utf8>>;
gleam_bool ->
<<"Bool"/utf8>>;
gleam_bit_array ->
<<"BitArray"/utf8>>;
gleam_time ->
<<"Time"/utf8>>;
gleam_date ->
<<"Date"/utf8>>;
gleam_time_of_day ->
<<"TimeOfDay"/utf8>>;
gleam_decimal ->
<<"Decimal"/utf8>>;
gleam_uuid ->
<<"Uuid"/utf8>>;
gleam_json ->
<<"Json"/utf8>>;
{gleam_list, Element_type} ->
<<<<"List("/utf8, (render_type(Element_type))/binary>>/binary,
")"/utf8>>;
{gleam_option, Inner_type} ->
<<<<"Option("/utf8, (render_type(Inner_type))/binary>>/binary,
")"/utf8>>;
{gleam_custom, _, Type_name} ->
Type_name
end.
-file("src/cquill/codegen/type_mapping.gleam", 464).
?DOC(" Render a GleamType as a fully qualified Gleam type string (with imports)\n").
-spec render_qualified_type(gleam_type()) -> binary().
render_qualified_type(Gleam_type) ->
case Gleam_type of
gleam_int ->
<<"Int"/utf8>>;
gleam_float ->
<<"Float"/utf8>>;
gleam_string ->
<<"String"/utf8>>;
gleam_bool ->
<<"Bool"/utf8>>;
gleam_bit_array ->
<<"BitArray"/utf8>>;
gleam_time ->
<<"birl.Time"/utf8>>;
gleam_date ->
<<"birl.Date"/utf8>>;
gleam_time_of_day ->
<<"birl.TimeOfDay"/utf8>>;
gleam_decimal ->
<<"decimal.Decimal"/utf8>>;
gleam_uuid ->
<<"uuid.Uuid"/utf8>>;
gleam_json ->
<<"json.Json"/utf8>>;
{gleam_list, Element_type} ->
<<<<"List("/utf8, (render_qualified_type(Element_type))/binary>>/binary,
")"/utf8>>;
{gleam_option, Inner_type} ->
<<<<"option.Option("/utf8,
(render_qualified_type(Inner_type))/binary>>/binary,
")"/utf8>>;
{gleam_custom, Module, Type_name} ->
<<<<Module/binary, "."/utf8>>/binary, Type_name/binary>>
end.
-file("src/cquill/codegen/type_mapping.gleam", 486).
?DOC(" Get the imports needed for a GleamType\n").
-spec get_imports(gleam_type()) -> list(binary()).
get_imports(Gleam_type) ->
case Gleam_type of
gleam_int ->
[];
gleam_float ->
[];
gleam_string ->
[];
gleam_bool ->
[];
gleam_bit_array ->
[];
gleam_time ->
[<<"birl"/utf8>>];
gleam_date ->
[<<"birl"/utf8>>];
gleam_time_of_day ->
[<<"birl"/utf8>>];
gleam_decimal ->
[<<"decimal"/utf8>>];
gleam_uuid ->
[<<"uuid"/utf8>>];
gleam_json ->
[<<"gleam/json"/utf8>>];
{gleam_list, Element_type} ->
get_imports(Element_type);
{gleam_option, Inner_type} ->
[<<"gleam/option"/utf8>> | get_imports(Inner_type)];
{gleam_custom, Module, _} ->
[Module]
end.
-file("src/cquill/codegen/type_mapping.gleam", 500).
?DOC(" Collect all unique imports needed for a list of column metadata\n").
-spec collect_imports(list(column_meta())) -> list(binary()).
collect_imports(Columns) ->
_pipe = Columns,
_pipe@1 = gleam@list:flat_map(
_pipe,
fun(Col) -> get_imports(erlang:element(2, Col)) end
),
_pipe@2 = gleam@list:unique(_pipe@1),
gleam@list:sort(_pipe@2, fun gleam@string:compare/2).
-file("src/cquill/codegen/type_mapping.gleam", 520).
?DOC(" Capitalize the first letter of a string\n").
-spec capitalize_first(binary()) -> binary().
capitalize_first(S) ->
case gleam_stdlib:string_pop_grapheme(S) of
{ok, {First, Rest}} ->
<<(string:uppercase(First))/binary, Rest/binary>>;
{error, _} ->
S
end.
-file("src/cquill/codegen/type_mapping.gleam", 512).
?DOC(" Convert snake_case to PascalCase\n").
-spec pascal_case(binary()) -> binary().
pascal_case(Input) ->
_pipe = Input,
_pipe@1 = gleam@string:split(_pipe, <<"_"/utf8>>),
_pipe@2 = gleam@list:map(_pipe@1, fun capitalize_first/1),
gleam@string:join(_pipe@2, <<""/utf8>>).
-file("src/cquill/codegen/type_mapping.gleam", 218).
?DOC(" Map array element types\n").
-spec map_element_type(binary(), list(binary())) -> {ok, gleam_type()} |
{error, type_mapping_error()}.
map_element_type(Element_udt, Enum_names) ->
Lowercase_udt = string:lowercase(Element_udt),
case Lowercase_udt of
<<"int4"/utf8>> ->
{ok, gleam_int};
<<"int"/utf8>> ->
{ok, gleam_int};
<<"integer"/utf8>> ->
{ok, gleam_int};
<<"int2"/utf8>> ->
{ok, gleam_int};
<<"smallint"/utf8>> ->
{ok, gleam_int};
<<"int8"/utf8>> ->
{ok, gleam_int};
<<"bigint"/utf8>> ->
{ok, gleam_int};
<<"float4"/utf8>> ->
{ok, gleam_float};
<<"real"/utf8>> ->
{ok, gleam_float};
<<"float8"/utf8>> ->
{ok, gleam_float};
<<"double precision"/utf8>> ->
{ok, gleam_float};
<<"numeric"/utf8>> ->
{ok, gleam_decimal};
<<"decimal"/utf8>> ->
{ok, gleam_decimal};
<<"varchar"/utf8>> ->
{ok, gleam_string};
<<"character varying"/utf8>> ->
{ok, gleam_string};
<<"bpchar"/utf8>> ->
{ok, gleam_string};
<<"char"/utf8>> ->
{ok, gleam_string};
<<"character"/utf8>> ->
{ok, gleam_string};
<<"text"/utf8>> ->
{ok, gleam_string};
<<"name"/utf8>> ->
{ok, gleam_string};
<<"bool"/utf8>> ->
{ok, gleam_bool};
<<"boolean"/utf8>> ->
{ok, gleam_bool};
<<"bytea"/utf8>> ->
{ok, gleam_bit_array};
<<"timestamp"/utf8>> ->
{ok, gleam_time};
<<"timestamptz"/utf8>> ->
{ok, gleam_time};
<<"date"/utf8>> ->
{ok, gleam_date};
<<"time"/utf8>> ->
{ok, gleam_time_of_day};
<<"timetz"/utf8>> ->
{ok, gleam_time_of_day};
<<"uuid"/utf8>> ->
{ok, gleam_uuid};
<<"json"/utf8>> ->
{ok, gleam_json};
<<"jsonb"/utf8>> ->
{ok, gleam_json};
_ ->
case gleam@list:contains(Enum_names, Element_udt) of
true ->
{ok,
{gleam_custom,
<<"schema/enums"/utf8>>,
pascal_case(Element_udt)}};
false ->
{error, {unknown_type, Element_udt, Element_udt}}
end
end.
-file("src/cquill/codegen/type_mapping.gleam", 199).
?DOC(" Map an array type based on its element type (from udt_name with _ prefix)\n").
-spec map_array_type(binary(), list(binary())) -> {ok, gleam_type()} |
{error, type_mapping_error()}.
map_array_type(Udt_name, Enum_names) ->
case gleam_stdlib:string_starts_with(Udt_name, <<"_"/utf8>>) of
true ->
Element_udt = gleam@string:drop_start(Udt_name, 1),
case map_element_type(Element_udt, Enum_names) of
{ok, Element_type} ->
{ok, {gleam_list, Element_type}};
{error, _} ->
{error, {unsupported_array, Element_udt}}
end;
false ->
{error, {unsupported_array, Udt_name}}
end.
-file("src/cquill/codegen/type_mapping.gleam", 279).
?DOC(" Map user-defined types (primarily enums)\n").
-spec map_user_defined_type(binary(), list(binary())) -> {ok, gleam_type()} |
{error, type_mapping_error()}.
map_user_defined_type(Udt_name, Enum_names) ->
case gleam@list:contains(Enum_names, Udt_name) of
true ->
{ok, {gleam_custom, <<"schema/enums"/utf8>>, pascal_case(Udt_name)}};
false ->
{ok, {gleam_custom, <<"schema/types"/utf8>>, pascal_case(Udt_name)}}
end.
-file("src/cquill/codegen/type_mapping.gleam", 295).
?DOC(" Try to map by udt_name when data_type is not specific enough\n").
-spec map_by_udt_name(binary(), list(binary())) -> {ok, gleam_type()} |
{error, type_mapping_error()}.
map_by_udt_name(Udt_name, Enum_names) ->
case Udt_name of
<<"int4"/utf8>> ->
{ok, gleam_int};
<<"int"/utf8>> ->
{ok, gleam_int};
<<"int2"/utf8>> ->
{ok, gleam_int};
<<"int8"/utf8>> ->
{ok, gleam_int};
<<"float4"/utf8>> ->
{ok, gleam_float};
<<"float8"/utf8>> ->
{ok, gleam_float};
<<"numeric"/utf8>> ->
{ok, gleam_decimal};
<<"varchar"/utf8>> ->
{ok, gleam_string};
<<"bpchar"/utf8>> ->
{ok, gleam_string};
<<"text"/utf8>> ->
{ok, gleam_string};
<<"name"/utf8>> ->
{ok, gleam_string};
<<"bool"/utf8>> ->
{ok, gleam_bool};
<<"bytea"/utf8>> ->
{ok, gleam_bit_array};
<<"timestamp"/utf8>> ->
{ok, gleam_time};
<<"timestamptz"/utf8>> ->
{ok, gleam_time};
<<"date"/utf8>> ->
{ok, gleam_date};
<<"time"/utf8>> ->
{ok, gleam_time_of_day};
<<"timetz"/utf8>> ->
{ok, gleam_time_of_day};
<<"uuid"/utf8>> ->
{ok, gleam_uuid};
<<"json"/utf8>> ->
{ok, gleam_json};
<<"jsonb"/utf8>> ->
{ok, gleam_json};
_ ->
case gleam_stdlib:string_starts_with(Udt_name, <<"_"/utf8>>) of
true ->
map_array_type(Udt_name, Enum_names);
false ->
case gleam@list:contains(Enum_names, Udt_name) of
true ->
{ok,
{gleam_custom,
<<"schema/enums"/utf8>>,
pascal_case(Udt_name)}};
false ->
{error,
{unknown_type, <<"unknown"/utf8>>, Udt_name}}
end
end
end.
-file("src/cquill/codegen/type_mapping.gleam", 128).
?DOC(" Map a PostgreSQL type to a base Gleam type (without Option wrapper)\n").
-spec map_base_type(binary(), binary(), list(binary())) -> {ok, gleam_type()} |
{error, type_mapping_error()}.
map_base_type(Data_type, Udt_name, Enum_names) ->
Lowercase_data_type = string:lowercase(Data_type),
Lowercase_udt_name = string:lowercase(Udt_name),
case Lowercase_data_type of
<<"integer"/utf8>> ->
{ok, gleam_int};
<<"int"/utf8>> ->
{ok, gleam_int};
<<"int4"/utf8>> ->
{ok, gleam_int};
<<"smallint"/utf8>> ->
{ok, gleam_int};
<<"int2"/utf8>> ->
{ok, gleam_int};
<<"bigint"/utf8>> ->
{ok, gleam_int};
<<"int8"/utf8>> ->
{ok, gleam_int};
<<"serial"/utf8>> ->
{ok, gleam_int};
<<"serial4"/utf8>> ->
{ok, gleam_int};
<<"smallserial"/utf8>> ->
{ok, gleam_int};
<<"serial2"/utf8>> ->
{ok, gleam_int};
<<"bigserial"/utf8>> ->
{ok, gleam_int};
<<"serial8"/utf8>> ->
{ok, gleam_int};
<<"real"/utf8>> ->
{ok, gleam_float};
<<"float4"/utf8>> ->
{ok, gleam_float};
<<"double precision"/utf8>> ->
{ok, gleam_float};
<<"float8"/utf8>> ->
{ok, gleam_float};
<<"numeric"/utf8>> ->
{ok, gleam_decimal};
<<"decimal"/utf8>> ->
{ok, gleam_decimal};
<<"character varying"/utf8>> ->
{ok, gleam_string};
<<"varchar"/utf8>> ->
{ok, gleam_string};
<<"character"/utf8>> ->
{ok, gleam_string};
<<"char"/utf8>> ->
{ok, gleam_string};
<<"bpchar"/utf8>> ->
{ok, gleam_string};
<<"text"/utf8>> ->
{ok, gleam_string};
<<"name"/utf8>> ->
{ok, gleam_string};
<<"boolean"/utf8>> ->
{ok, gleam_bool};
<<"bool"/utf8>> ->
{ok, gleam_bool};
<<"bytea"/utf8>> ->
{ok, gleam_bit_array};
<<"timestamp without time zone"/utf8>> ->
{ok, gleam_time};
<<"timestamp"/utf8>> ->
{ok, gleam_time};
<<"timestamp with time zone"/utf8>> ->
{ok, gleam_time};
<<"timestamptz"/utf8>> ->
{ok, gleam_time};
<<"date"/utf8>> ->
{ok, gleam_date};
<<"time without time zone"/utf8>> ->
{ok, gleam_time_of_day};
<<"time"/utf8>> ->
{ok, gleam_time_of_day};
<<"time with time zone"/utf8>> ->
{ok, gleam_time_of_day};
<<"timetz"/utf8>> ->
{ok, gleam_time_of_day};
<<"interval"/utf8>> ->
{ok, gleam_string};
<<"uuid"/utf8>> ->
{ok, gleam_uuid};
<<"json"/utf8>> ->
{ok, gleam_json};
<<"jsonb"/utf8>> ->
{ok, gleam_json};
<<"array"/utf8>> ->
map_array_type(Lowercase_udt_name, Enum_names);
<<"user-defined"/utf8>> ->
map_user_defined_type(Udt_name, Enum_names);
_ ->
map_by_udt_name(Lowercase_udt_name, Enum_names)
end.
-file("src/cquill/codegen/type_mapping.gleam", 109).
?DOC(
" Map a PostgreSQL column to its corresponding Gleam type\n"
"\n"
" Parameters:\n"
" - data_type: The PostgreSQL data type (e.g., \"integer\", \"character varying\")\n"
" - udt_name: The underlying type name (e.g., \"int4\", \"varchar\", or enum name)\n"
" - is_nullable: Whether the column allows NULL values\n"
" - enum_names: List of known enum type names in the schema\n"
"\n"
" Returns:\n"
" - Ok(GleamType) with the mapped type (wrapped in Option if nullable)\n"
" - Error(TypeMappingError) if the type cannot be mapped\n"
).
-spec map_postgres_type(binary(), binary(), boolean(), list(binary())) -> {ok,
gleam_type()} |
{error, type_mapping_error()}.
map_postgres_type(Data_type, Udt_name, Is_nullable, Enum_names) ->
gleam@result:'try'(
map_base_type(Data_type, Udt_name, Enum_names),
fun(Base_type) ->
Final_type = case Is_nullable of
true ->
{gleam_option, Base_type};
false ->
Base_type
end,
{ok, Final_type}
end
).
-file("src/cquill/codegen/type_mapping.gleam", 375).
?DOC(
" Map an introspected column to full column metadata\n"
"\n"
" Parameters:\n"
" - column_name: The column name\n"
" - data_type: PostgreSQL data type\n"
" - udt_name: Underlying type name\n"
" - is_nullable: Whether the column allows NULL\n"
" - column_default: Default value expression (if any)\n"
" - enum_names: List of known enum type names\n"
"\n"
" Returns:\n"
" - Ok(ColumnMeta) with full metadata\n"
" - Error(TypeMappingError) if type cannot be mapped\n"
).
-spec map_column(
binary(),
binary(),
binary(),
boolean(),
gleam@option:option(binary()),
list(binary())
) -> {ok, column_meta()} | {error, type_mapping_error()}.
map_column(
Column_name,
Data_type,
Udt_name,
Is_nullable,
Column_default,
Enum_names
) ->
gleam@result:'try'(
map_postgres_type(Data_type, Udt_name, Is_nullable, Enum_names),
fun(Gleam_type) ->
Is_auto_generated = detect_auto_generated(Data_type, Column_default),
{ok,
{column_meta,
Gleam_type,
Is_auto_generated,
Data_type,
Udt_name,
Column_name,
Is_nullable}}
end
).
-file("src/cquill/codegen/type_mapping.gleam", 541).
?DOC(" Check if a character is uppercase\n").
-spec is_uppercase(binary()) -> boolean().
is_uppercase(Char) ->
(Char =:= string:uppercase(Char)) andalso (Char /= string:lowercase(Char)).
-file("src/cquill/codegen/type_mapping.gleam", 528).
?DOC(" Convert PascalCase or snake_case to snake_case\n").
-spec snake_case(binary()) -> binary().
snake_case(Input) ->
_pipe = Input,
_pipe@1 = gleam@string:to_graphemes(_pipe),
gleam@list:index_fold(
_pipe@1,
<<""/utf8>>,
fun(Acc, Char, Index) -> case {is_uppercase(Char), Index} of
{true, 0} ->
string:lowercase(Char);
{true, _} ->
<<<<Acc/binary, "_"/utf8>>/binary,
(string:lowercase(Char))/binary>>;
{false, _} ->
<<Acc/binary, Char/binary>>
end end
).
-file("src/cquill/codegen/type_mapping.gleam", 550).
?DOC(" Check if a GleamType is nullable (wrapped in Option)\n").
-spec is_nullable_type(gleam_type()) -> boolean().
is_nullable_type(Gleam_type) ->
case Gleam_type of
{gleam_option, _} ->
true;
_ ->
false
end.
-file("src/cquill/codegen/type_mapping.gleam", 558).
?DOC(" Check if a GleamType is a list type\n").
-spec is_list_type(gleam_type()) -> boolean().
is_list_type(Gleam_type) ->
case Gleam_type of
{gleam_list, _} ->
true;
_ ->
false
end.
-file("src/cquill/codegen/type_mapping.gleam", 566).
?DOC(" Check if a GleamType is a custom type (enum or user-defined)\n").
-spec is_custom_type(gleam_type()) -> boolean().
is_custom_type(Gleam_type) ->
case Gleam_type of
{gleam_custom, _, _} ->
true;
{gleam_option, {gleam_custom, _, _}} ->
true;
{gleam_list, {gleam_custom, _, _}} ->
true;
_ ->
false
end.
-file("src/cquill/codegen/type_mapping.gleam", 576).
?DOC(" Get the inner type from an Option type\n").
-spec unwrap_option(gleam_type()) -> gleam_type().
unwrap_option(Gleam_type) ->
case Gleam_type of
{gleam_option, Inner} ->
Inner;
Other ->
Other
end.
-file("src/cquill/codegen/type_mapping.gleam", 584).
?DOC(" Get the element type from a List type\n").
-spec unwrap_list(gleam_type()) -> gleam_type().
unwrap_list(Gleam_type) ->
case Gleam_type of
{gleam_list, Element} ->
Element;
Other ->
Other
end.
-file("src/cquill/codegen/type_mapping.gleam", 592).
?DOC(" Check if two GleamTypes are equal\n").
-spec types_equal(gleam_type(), gleam_type()) -> boolean().
types_equal(Type_a, Type_b) ->
case {Type_a, Type_b} of
{gleam_int, gleam_int} ->
true;
{gleam_float, gleam_float} ->
true;
{gleam_string, gleam_string} ->
true;
{gleam_bool, gleam_bool} ->
true;
{gleam_bit_array, gleam_bit_array} ->
true;
{gleam_time, gleam_time} ->
true;
{gleam_date, gleam_date} ->
true;
{gleam_time_of_day, gleam_time_of_day} ->
true;
{gleam_decimal, gleam_decimal} ->
true;
{gleam_uuid, gleam_uuid} ->
true;
{gleam_json, gleam_json} ->
true;
{{gleam_list, A}, {gleam_list, B}} ->
types_equal(A, B);
{{gleam_option, A@1}, {gleam_option, B@1}} ->
types_equal(A@1, B@1);
{{gleam_custom, Mod_a, Name_a}, {gleam_custom, Mod_b, Name_b}} ->
(Mod_a =:= Mod_b) andalso (Name_a =:= Name_b);
{_, _} ->
false
end.