-module(sift@string). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sift/string.gleam"). -export([min_length/2, max_length/2, length/2, non_empty/1, matches/2, starts_with/2, ends_with/2, contains/2, email/1, url/1, uuid/1, numeric/1, alpha/1, alphanumeric/1, trimmed/1, one_of/2]). -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(" String validators — length, pattern matching, format checks (email, url, uuid).\n"). -file("src/sift/string.gleam", 7). ?DOC(" String must have at least n graphemes\n"). -spec min_length(integer(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). min_length(N, Msg) -> fun(Value) -> case string:length(Value) >= N of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 17). ?DOC(" String must have at most n graphemes\n"). -spec max_length(integer(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). max_length(N, Msg) -> fun(Value) -> case string:length(Value) =< N of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 27). ?DOC(" String must have exactly n graphemes\n"). -spec length(integer(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). length(N, Msg) -> fun(Value) -> case string:length(Value) =:= N of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 37). ?DOC(" String must not be empty (shorthand for min_length(1))\n"). -spec non_empty(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). non_empty(Msg) -> min_length(1, Msg). -file("src/sift/string.gleam", 42). ?DOC(" String must match the given regex pattern\n"). -spec matches(binary(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). matches(Pattern, Msg) -> fun(Value) -> case gleam@regexp:from_string(Pattern) of {ok, Re} -> case gleam@regexp:check(Re, Value) of true -> {ok, Value}; false -> {error, Msg} end; {error, _} -> {error, Msg} end end. -file("src/sift/string.gleam", 72). ?DOC(" String must start with the given prefix\n"). -spec starts_with(binary(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). starts_with(Prefix, Msg) -> fun(Value) -> case gleam_stdlib:string_starts_with(Value, Prefix) of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 85). ?DOC(" String must end with the given suffix\n"). -spec ends_with(binary(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). ends_with(Suffix, Msg) -> fun(Value) -> case gleam_stdlib:string_ends_with(Value, Suffix) of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 98). ?DOC(" String must contain the given substring\n"). -spec contains(binary(), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). contains(Substring, Msg) -> fun(Value) -> case gleam_stdlib:contains_string(Value, Substring) of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 117). ?DOC( " Must look like an email (contains exactly one @, something before and after).\n" "\n" " ```gleam\n" " let validator = string.email(\"invalid email\")\n" " validator(\"jo@example.com\") // -> Ok(\"jo@example.com\")\n" " validator(\"nope\") // -> Error(\"invalid email\")\n" " ```\n" ). -spec email(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). email(Msg) -> matches(<<"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"/utf8>>, Msg). -file("src/sift/string.gleam", 122). ?DOC(" Must look like a URL (http:// or https://)\n"). -spec url(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). url(Msg) -> matches(<<"^https?://[^\\s]+$"/utf8>>, Msg). -file("src/sift/string.gleam", 127). ?DOC(" Must be a valid UUID v4 format\n"). -spec uuid(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). uuid(Msg) -> matches( <<"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"/utf8>>, Msg ). -file("src/sift/string.gleam", 141). ?DOC( " All characters must be digits (0-9).\n" "\n" " ```gleam\n" " let validator = string.numeric(\"digits only\")\n" " validator(\"123\") // -> Ok(\"123\")\n" " validator(\"12a\") // -> Error(\"digits only\")\n" " ```\n" ). -spec numeric(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). numeric(Msg) -> matches(<<"^[0-9]+$"/utf8>>, Msg). -file("src/sift/string.gleam", 152). ?DOC( " All characters must be letters (a-zA-Z).\n" "\n" " ```gleam\n" " let validator = string.alpha(\"letters only\")\n" " validator(\"abc\") // -> Ok(\"abc\")\n" " validator(\"abc1\") // -> Error(\"letters only\")\n" " ```\n" ). -spec alpha(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). alpha(Msg) -> matches(<<"^[a-zA-Z]+$"/utf8>>, Msg). -file("src/sift/string.gleam", 163). ?DOC( " All characters must be letters or digits.\n" "\n" " ```gleam\n" " let validator = string.alphanumeric(\"alphanumeric only\")\n" " validator(\"abc123\") // -> Ok(\"abc123\")\n" " validator(\"abc 123\") // -> Error(\"alphanumeric only\")\n" " ```\n" ). -spec alphanumeric(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). alphanumeric(Msg) -> matches(<<"^[a-zA-Z0-9]+$"/utf8>>, Msg). -file("src/sift/string.gleam", 174). ?DOC( " String must have no leading or trailing whitespace.\n" "\n" " ```gleam\n" " let validator = string.trimmed(\"no surrounding spaces\")\n" " validator(\"hello\") // -> Ok(\"hello\")\n" " validator(\" hello\") // -> Error(\"no surrounding spaces\")\n" " ```\n" ). -spec trimmed(binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). trimmed(Msg) -> fun(Value) -> case gleam@string:trim(Value) =:= Value of true -> {ok, Value}; false -> {error, Msg} end end. -file("src/sift/string.gleam", 183). -spec list_contains(list(binary()), binary()) -> boolean(). list_contains(Items, Target) -> case Items of [] -> false; [First | Rest] -> case First =:= Target of true -> true; false -> list_contains(Rest, Target) end end. -file("src/sift/string.gleam", 59). ?DOC(" String must be one of the given values\n"). -spec one_of(list(binary()), binary()) -> fun((binary()) -> {ok, binary()} | {error, binary()}). one_of(Values, Msg) -> fun(Value) -> case list_contains(Values, Value) of true -> {ok, Value}; false -> {error, Msg} end end.