-module(cquill@changeset). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/cquill/changeset.gleam"). -export([new/1, change/2, empty/0, put_change/3, delete_change/2, get_change/2, add_error/4, clear_errors/2, force_change/3, clear_all_errors/1, get_errors/2, all_errors/1, has_error/2, has_errors/1, error_count/1, apply/1, is_valid/1, get_changes/1, get_data/1, merge_changes/1, validate_if_present/3, validate_required/2, validate_format/3, validate_length_min/3, validate_length_max/3, validate_length_exact/3, validate_length/4, validate_inclusion/3, validate_exclusion/3, validate_number_range/4, validate_confirmation/3, validate_acceptance/2, validate_custom/4, format_errors/1, format_field_errors/2]). -export_type([changeset/0, validation_error/0, validation_type/0, length_constraint/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 changeset() :: {changeset, gleam@option:option(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())), gleam@dict:dict(binary(), gleam@dynamic:dynamic_()), gleam@dict:dict(binary(), list(validation_error())), boolean(), list(binary())}. -type validation_error() :: {validation_error, binary(), validation_type()}. -type validation_type() :: required | format | {length, length_constraint()} | inclusion | exclusion | range | {custom, binary()} | uniqueness | confirmation | acceptance. -type length_constraint() :: {min_length, integer()} | {max_length, integer()} | {exact_length, integer()} | {range_length, integer(), integer()}. -file("src/cquill/changeset.gleam", 108). ?DOC(" Create a new changeset from input data (for inserts)\n"). -spec new(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> changeset(). new(Changes) -> {changeset, none, Changes, maps:new(), true, []}. -file("src/cquill/changeset.gleam", 119). ?DOC(" Create a changeset for updating existing data\n"). -spec change( gleam@dict:dict(binary(), gleam@dynamic:dynamic_()), gleam@dict:dict(binary(), gleam@dynamic:dynamic_()) ) -> changeset(). change(Existing, Changes) -> {changeset, {some, Existing}, Changes, maps:new(), true, []}. -file("src/cquill/changeset.gleam", 133). ?DOC(" Create an empty changeset (useful for forms)\n"). -spec empty() -> changeset(). empty() -> {changeset, none, maps:new(), maps:new(), true, []}. -file("src/cquill/changeset.gleam", 408). ?DOC(" Put a change into the changeset\n"). -spec put_change(changeset(), binary(), gleam@dynamic:dynamic_()) -> changeset(). put_change(Changeset, Field, Value) -> {changeset, erlang:element(2, Changeset), gleam@dict:insert(erlang:element(3, Changeset), Field, Value), erlang:element(4, Changeset), erlang:element(5, Changeset), erlang:element(6, Changeset)}. -file("src/cquill/changeset.gleam", 417). ?DOC(" Delete a change from the changeset\n"). -spec delete_change(changeset(), binary()) -> changeset(). delete_change(Changeset, Field) -> {changeset, erlang:element(2, Changeset), gleam@dict:delete(erlang:element(3, Changeset), Field), erlang:element(4, Changeset), erlang:element(5, Changeset), erlang:element(6, Changeset)}. -file("src/cquill/changeset.gleam", 422). ?DOC(" Get a change value from the changeset\n"). -spec get_change(changeset(), binary()) -> gleam@option:option(gleam@dynamic:dynamic_()). get_change(Changeset, Field) -> _pipe = gleam_stdlib:map_get(erlang:element(3, Changeset), Field), gleam@option:from_result(_pipe). -file("src/cquill/changeset.gleam", 444). ?DOC(" Add an error to the changeset\n"). -spec add_error(changeset(), binary(), binary(), validation_type()) -> changeset(). add_error(Changeset, Field, Message, Validation) -> Error = {validation_error, Message, Validation}, Existing_errors = begin _pipe = gleam_stdlib:map_get(erlang:element(4, Changeset), Field), gleam@result:unwrap(_pipe, []) end, Updated_errors = gleam@dict:insert( erlang:element(4, Changeset), Field, [Error | Existing_errors] ), {changeset, erlang:element(2, Changeset), erlang:element(3, Changeset), Updated_errors, false, erlang:element(6, Changeset)}. -file("src/cquill/changeset.gleam", 460). ?DOC(" Clear all errors for a specific field\n"). -spec clear_errors(changeset(), binary()) -> changeset(). clear_errors(Changeset, Field) -> Updated_errors = gleam@dict:delete(erlang:element(4, Changeset), Field), Is_valid = gleam@dict:is_empty(Updated_errors), {changeset, erlang:element(2, Changeset), erlang:element(3, Changeset), Updated_errors, Is_valid, erlang:element(6, Changeset)}. -file("src/cquill/changeset.gleam", 429). ?DOC( " Force a change even if validation would fail\n" " Use with caution — this bypasses validation for the field\n" ). -spec force_change(changeset(), binary(), gleam@dynamic:dynamic_()) -> changeset(). force_change(Changeset, Field, Value) -> _pipe = Changeset, _pipe@1 = put_change(_pipe, Field, Value), clear_errors(_pipe@1, Field). -file("src/cquill/changeset.gleam", 467). ?DOC(" Clear all errors from the changeset\n"). -spec clear_all_errors(changeset()) -> changeset(). clear_all_errors(Changeset) -> {changeset, erlang:element(2, Changeset), erlang:element(3, Changeset), maps:new(), true, erlang:element(6, Changeset)}. -file("src/cquill/changeset.gleam", 472). ?DOC(" Get errors for a specific field\n"). -spec get_errors(changeset(), binary()) -> list(validation_error()). get_errors(Changeset, Field) -> _pipe = gleam_stdlib:map_get(erlang:element(4, Changeset), Field), gleam@result:unwrap(_pipe, []). -file("src/cquill/changeset.gleam", 478). ?DOC(" Get all errors as a list of (field, error) tuples\n"). -spec all_errors(changeset()) -> list({binary(), validation_error()}). all_errors(Changeset) -> _pipe = maps:to_list(erlang:element(4, Changeset)), gleam@list:flat_map( _pipe, fun(Pair) -> {Field, Errors} = Pair, gleam@list:map(Errors, fun(Err) -> {Field, Err} end) end ). -file("src/cquill/changeset.gleam", 487). ?DOC(" Check if a specific field has errors\n"). -spec has_error(changeset(), binary()) -> boolean(). has_error(Changeset, Field) -> case gleam_stdlib:map_get(erlang:element(4, Changeset), Field) of {ok, Errors} -> not gleam@list:is_empty(Errors); {error, _} -> false end. -file("src/cquill/changeset.gleam", 496). ?DOC( " Check if the changeset has any validation errors\n" " This is a convenience function to check if any field has errors\n" ). -spec has_errors(changeset()) -> boolean(). has_errors(Changeset) -> not gleam@dict:is_empty(erlang:element(4, Changeset)). -file("src/cquill/changeset.gleam", 502). ?DOC( " Count the total number of validation errors across all fields\n" " Returns the sum of all errors on all fields\n" ). -spec error_count(changeset()) -> integer(). error_count(Changeset) -> _pipe = erlang:element(4, Changeset), _pipe@1 = maps:values(_pipe), _pipe@2 = gleam@list:map(_pipe@1, fun erlang:length/1), gleam@int:sum(_pipe@2). -file("src/cquill/changeset.gleam", 515). ?DOC( " Apply the changeset and return the result\n" " Returns Ok(changes) if valid, Error(errors) if invalid\n" ). -spec apply(changeset()) -> {ok, gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} | {error, gleam@dict:dict(binary(), list(validation_error()))}. apply(Changeset) -> case erlang:element(5, Changeset) of true -> {ok, erlang:element(3, Changeset)}; false -> {error, erlang:element(4, Changeset)} end. -file("src/cquill/changeset.gleam", 525). ?DOC(" Check if the changeset is valid\n"). -spec is_valid(changeset()) -> boolean(). is_valid(Changeset) -> erlang:element(5, Changeset). -file("src/cquill/changeset.gleam", 530). ?DOC(" Get the changes from the changeset (regardless of validity)\n"). -spec get_changes(changeset()) -> gleam@dict:dict(binary(), gleam@dynamic:dynamic_()). get_changes(Changeset) -> erlang:element(3, Changeset). -file("src/cquill/changeset.gleam", 535). ?DOC(" Get the original data (if this is an update changeset)\n"). -spec get_data(changeset()) -> gleam@option:option(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())). get_data(Changeset) -> erlang:element(2, Changeset). -file("src/cquill/changeset.gleam", 541). ?DOC( " Merge changes from the changeset with the original data\n" " For updates, this combines old data with new changes\n" ). -spec merge_changes(changeset()) -> gleam@dict:dict(binary(), gleam@dynamic:dynamic_()). merge_changes(Changeset) -> case erlang:element(2, Changeset) of none -> erlang:element(3, Changeset); {some, Data} -> maps:merge(Data, erlang:element(3, Changeset)) end. -file("src/cquill/changeset.gleam", 553). ?DOC(" Get a field value from changes or original data\n"). -spec get_field(changeset(), binary()) -> gleam@option:option(gleam@dynamic:dynamic_()). get_field(Changeset, Field) -> case gleam_stdlib:map_get(erlang:element(3, Changeset), Field) of {ok, Value} -> {some, Value}; {error, _} -> case erlang:element(2, Changeset) of none -> none; {some, Data} -> _pipe = gleam_stdlib:map_get(Data, Field), gleam@option:from_result(_pipe) end end. -file("src/cquill/changeset.gleam", 392). ?DOC( " Validate a field only if it is present (non-nil)\n" " Useful for optional fields that have constraints when provided\n" ). -spec validate_if_present( changeset(), binary(), fun((changeset()) -> changeset()) ) -> changeset(). validate_if_present(Changeset, Field, Validator) -> case get_field(Changeset, Field) of none -> Changeset; {some, _} -> Validator(Changeset) end. -file("src/cquill/changeset.gleam", 568). ?DOC(" Get a field value as a string\n"). -spec get_field_string(changeset(), binary()) -> gleam@option:option(binary()). get_field_string(Changeset, Field) -> case get_field(Changeset, Field) of none -> none; {some, Value} -> case gleam@dynamic@decode:run( Value, {decoder, fun gleam@dynamic@decode:decode_string/1} ) of {ok, S} -> {some, S}; {error, _} -> none end end. -file("src/cquill/changeset.gleam", 581). ?DOC(" Get a field value as an int\n"). -spec get_field_int(changeset(), binary()) -> gleam@option:option(integer()). get_field_int(Changeset, Field) -> case get_field(Changeset, Field) of none -> none; {some, Value} -> case gleam@dynamic@decode:run( Value, {decoder, fun gleam@dynamic@decode:decode_int/1} ) of {ok, I} -> {some, I}; {error, _} -> none end end. -file("src/cquill/changeset.gleam", 594). ?DOC(" Get a field value as a bool\n"). -spec get_field_bool(changeset(), binary()) -> gleam@option:option(boolean()). get_field_bool(Changeset, Field) -> case get_field(Changeset, Field) of none -> none; {some, Value} -> case gleam@dynamic@decode:run( Value, {decoder, fun gleam@dynamic@decode:decode_bool/1} ) of {ok, B} -> {some, B}; {error, _} -> none end end. -file("src/cquill/changeset.gleam", 607). ?DOC(" Check if a value is present (not None and not empty string)\n"). -spec is_present(gleam@option:option(gleam@dynamic:dynamic_())) -> boolean(). is_present(Value) -> case Value of none -> false; {some, V} -> case gleam@dynamic@decode:run( V, {decoder, fun gleam@dynamic@decode:decode_string/1} ) of {ok, S} -> not gleam@string:is_empty(S); {error, _} -> true end end. -file("src/cquill/changeset.gleam", 620). ?DOC(" Mark a field as validated\n"). -spec mark_validated(changeset(), binary()) -> changeset(). mark_validated(Changeset, Field) -> case gleam@list:contains(erlang:element(6, Changeset), Field) of true -> Changeset; false -> {changeset, erlang:element(2, Changeset), erlang:element(3, Changeset), erlang:element(4, Changeset), erlang:element(5, Changeset), [Field | erlang:element(6, Changeset)]} end. -file("src/cquill/changeset.gleam", 148). ?DOC(" Validate that the specified fields are present and non-empty\n"). -spec validate_required(changeset(), list(binary())) -> changeset(). validate_required(Changeset, Fields) -> gleam@list:fold( Fields, Changeset, fun(Cs, Field) -> Value = get_field(Cs, Field), case is_present(Value) of true -> mark_validated(Cs, Field); false -> add_error(Cs, Field, <<"is required"/utf8>>, required) end end ). -file("src/cquill/changeset.gleam", 162). ?DOC(" Validate that a string field matches a regex pattern\n"). -spec validate_format(changeset(), binary(), binary()) -> changeset(). validate_format(Changeset, Field, Pattern) -> case get_field_string(Changeset, Field) of none -> mark_validated(Changeset, Field); {some, Value} -> case gleam@regexp:from_string(Pattern) of {error, _} -> add_error( Changeset, Field, <<"has invalid format"/utf8>>, format ); {ok, Re} -> case gleam@regexp:check(Re, Value) of true -> mark_validated(Changeset, Field); false -> add_error( Changeset, Field, <<"has invalid format"/utf8>>, format ) end end end. -file("src/cquill/changeset.gleam", 220). -spec validate_length_impl( changeset(), binary(), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(integer()) ) -> changeset(). validate_length_impl(Changeset, Field, Min, Max, Exact) -> case get_field_string(Changeset, Field) of none -> mark_validated(Changeset, Field); {some, Value} -> Len = string:length(Value), case Exact of {some, Exact_len} when Len =/= Exact_len -> add_error( Changeset, Field, <<<<"must be exactly "/utf8, (erlang:integer_to_binary(Exact_len))/binary>>/binary, " characters"/utf8>>, {length, {exact_length, Exact_len}} ); {some, _} -> mark_validated(Changeset, Field); none -> case {Min, Max} of {{some, Min_len}, {some, Max_len}} when (Len < Min_len) orelse (Len > Max_len) -> add_error( Changeset, Field, <<<<<<<<"must be between "/utf8, (erlang:integer_to_binary( Min_len ))/binary>>/binary, " and "/utf8>>/binary, (erlang:integer_to_binary(Max_len))/binary>>/binary, " characters"/utf8>>, {length, {range_length, Min_len, Max_len}} ); {{some, Min_len@1}, none} when Len < Min_len@1 -> add_error( Changeset, Field, <<<<"must be at least "/utf8, (erlang:integer_to_binary(Min_len@1))/binary>>/binary, " characters"/utf8>>, {length, {min_length, Min_len@1}} ); {none, {some, Max_len@1}} when Len > Max_len@1 -> add_error( Changeset, Field, <<<<"must be at most "/utf8, (erlang:integer_to_binary(Max_len@1))/binary>>/binary, " characters"/utf8>>, {length, {max_length, Max_len@1}} ); {_, _} -> mark_validated(Changeset, Field) end end end. -file("src/cquill/changeset.gleam", 184). ?DOC(" Validate string length with minimum constraint\n"). -spec validate_length_min(changeset(), binary(), integer()) -> changeset(). validate_length_min(Changeset, Field, Min) -> validate_length_impl(Changeset, Field, {some, Min}, none, none). -file("src/cquill/changeset.gleam", 193). ?DOC(" Validate string length with maximum constraint\n"). -spec validate_length_max(changeset(), binary(), integer()) -> changeset(). validate_length_max(Changeset, Field, Max) -> validate_length_impl(Changeset, Field, none, {some, Max}, none). -file("src/cquill/changeset.gleam", 202). ?DOC(" Validate string length with exact length constraint\n"). -spec validate_length_exact(changeset(), binary(), integer()) -> changeset(). validate_length_exact(Changeset, Field, Length) -> validate_length_impl(Changeset, Field, none, none, {some, Length}). -file("src/cquill/changeset.gleam", 211). ?DOC(" Validate string length with min and max constraints\n"). -spec validate_length(changeset(), binary(), integer(), integer()) -> changeset(). validate_length(Changeset, Field, Min, Max) -> validate_length_impl(Changeset, Field, {some, Min}, {some, Max}, none). -file("src/cquill/changeset.gleam", 276). ?DOC(" Validate that a field value is in a list of allowed values\n"). -spec validate_inclusion(changeset(), binary(), list(binary())) -> changeset(). validate_inclusion(Changeset, Field, Allowed) -> case get_field_string(Changeset, Field) of none -> mark_validated(Changeset, Field); {some, Value} -> case gleam@list:contains(Allowed, Value) of true -> mark_validated(Changeset, Field); false -> add_error( Changeset, Field, <<"is not a valid option"/utf8>>, inclusion ) end end. -file("src/cquill/changeset.gleam", 293). ?DOC(" Validate that a field value is NOT in a list of excluded values\n"). -spec validate_exclusion(changeset(), binary(), list(binary())) -> changeset(). validate_exclusion(Changeset, Field, Excluded) -> case get_field_string(Changeset, Field) of none -> mark_validated(Changeset, Field); {some, Value} -> case gleam@list:contains(Excluded, Value) of true -> add_error( Changeset, Field, <<"is not allowed"/utf8>>, exclusion ); false -> mark_validated(Changeset, Field) end end. -file("src/cquill/changeset.gleam", 310). ?DOC(" Validate that a numeric field is within a range\n"). -spec validate_number_range( changeset(), binary(), gleam@option:option(integer()), gleam@option:option(integer()) ) -> changeset(). validate_number_range(Changeset, Field, Min, Max) -> case get_field_int(Changeset, Field) of none -> mark_validated(Changeset, Field); {some, Value} -> case {Min, Max} of {{some, Min_val}, {some, Max_val}} when (Value < Min_val) orelse (Value > Max_val) -> add_error( Changeset, Field, <<<<<<"must be between "/utf8, (erlang:integer_to_binary(Min_val))/binary>>/binary, " and "/utf8>>/binary, (erlang:integer_to_binary(Max_val))/binary>>, range ); {{some, Min_val@1}, none} when Value < Min_val@1 -> add_error( Changeset, Field, <<"must be at least "/utf8, (erlang:integer_to_binary(Min_val@1))/binary>>, range ); {none, {some, Max_val@1}} when Value > Max_val@1 -> add_error( Changeset, Field, <<"must be at most "/utf8, (erlang:integer_to_binary(Max_val@1))/binary>>, range ); {_, _} -> mark_validated(Changeset, Field) end end. -file("src/cquill/changeset.gleam", 352). ?DOC( " Validate that a confirmation field matches the original field\n" " e.g., password and password_confirmation\n" ). -spec validate_confirmation(changeset(), binary(), binary()) -> changeset(). validate_confirmation(Changeset, Field, Confirmation_field) -> Original = get_field_string(Changeset, Field), Confirmation = get_field_string(Changeset, Confirmation_field), case {Original, Confirmation} of {{some, Orig}, {some, Conf}} when Orig =/= Conf -> add_error( Changeset, Confirmation_field, <<"does not match"/utf8>>, confirmation ); {_, _} -> mark_validated(Changeset, Confirmation_field) end. -file("src/cquill/changeset.gleam", 368). ?DOC(" Validate that a boolean field is true (for accepting terms, etc.)\n"). -spec validate_acceptance(changeset(), binary()) -> changeset(). validate_acceptance(Changeset, Field) -> case get_field_bool(Changeset, Field) of {some, true} -> mark_validated(Changeset, Field); {some, false} -> add_error(Changeset, Field, <<"must be accepted"/utf8>>, acceptance); none -> add_error(Changeset, Field, <<"must be accepted"/utf8>>, acceptance) end. -file("src/cquill/changeset.gleam", 378). ?DOC( " Apply a custom validation function\n" " The function should return Ok(Nil) if valid, or Error(message) if invalid\n" ). -spec validate_custom( changeset(), binary(), binary(), fun((changeset()) -> {ok, nil} | {error, binary()}) ) -> changeset(). validate_custom(Changeset, Field, Name, Validator) -> case Validator(Changeset) of {ok, _} -> mark_validated(Changeset, Field); {error, Message} -> add_error(Changeset, Field, Message, {custom, Name}) end. -file("src/cquill/changeset.gleam", 636). ?DOC(" Format all errors as a human-readable string\n"). -spec format_errors(changeset()) -> binary(). format_errors(Changeset) -> _pipe = all_errors(Changeset), _pipe@1 = gleam@list:map( _pipe, fun(Pair) -> {Field, Error} = Pair, <<<>/binary, (erlang:element(2, Error))/binary>> end ), gleam@string:join(_pipe@1, <<", "/utf8>>). -file("src/cquill/changeset.gleam", 646). ?DOC(" Format errors for a specific field\n"). -spec format_field_errors(changeset(), binary()) -> binary(). format_field_errors(Changeset, Field) -> _pipe = get_errors(Changeset, Field), _pipe@1 = gleam@list:map(_pipe, fun(Error) -> erlang:element(2, Error) end), gleam@string:join(_pipe@1, <<", "/utf8>>).