Current section
Files
Jump to
Current section
Files
src/envie@decode.erl
-module(envie@decode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/envie/decode.gleam").
-export([string/0, int/0, float/0, bool/0, map/2, then/2, validated/2, int_range/2, float_range/2, url/0, url_with_scheme/1, web_url/0, string_prefix/1, non_empty_string/0, string_list/1, int_list/1, port/0, one_of/1]).
-export_type([decoder/1]).
-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 decoder(DKX) :: {decoder,
fun((binary()) -> {ok, DKX} | {error, binary()})}.
-file("src/envie/decode.gleam", 22).
?DOC(" Accept any string as-is.\n").
-spec string() -> decoder(binary()).
string() ->
{decoder, fun(S) -> {ok, S} end}.
-file("src/envie/decode.gleam", 27).
?DOC(" Parse an integer.\n").
-spec int() -> decoder(integer()).
int() ->
{decoder, fun(S) -> _pipe = gleam_stdlib:parse_int(S),
gleam@result:replace_error(
_pipe,
<<"Expected integer, got: "/utf8, S/binary>>
) end}.
-file("src/envie/decode.gleam", 35).
?DOC(" Parse a float.\n").
-spec float() -> decoder(float()).
float() ->
{decoder, fun(S) -> _pipe = gleam_stdlib:parse_float(S),
gleam@result:replace_error(
_pipe,
<<"Expected float, got: "/utf8, S/binary>>
) end}.
-file("src/envie/decode.gleam", 43).
?DOC(" Parse a boolean. Accepts (case-insensitive): true/yes/1/on → True; false/no/0/off → False.\n").
-spec bool() -> decoder(boolean()).
bool() ->
{decoder, fun(S) -> case string:lowercase(S) of
<<"true"/utf8>> ->
{ok, true};
<<"yes"/utf8>> ->
{ok, true};
<<"1"/utf8>> ->
{ok, true};
<<"on"/utf8>> ->
{ok, true};
<<"false"/utf8>> ->
{ok, false};
<<"no"/utf8>> ->
{ok, false};
<<"0"/utf8>> ->
{ok, false};
<<"off"/utf8>> ->
{ok, false};
_ ->
{error, <<"Expected boolean, got: "/utf8, S/binary>>}
end end}.
-file("src/envie/decode.gleam", 58).
?DOC(" Transform the decoded value with a pure function.\n").
-spec map(decoder(DLC), fun((DLC) -> DLE)) -> decoder(DLE).
map(Decoder, F) ->
{decoder,
fun(S) ->
gleam@result:'try'(
(erlang:element(2, Decoder))(S),
fun(Value) -> {ok, F(Value)} end
)
end}.
-file("src/envie/decode.gleam", 66).
?DOC(" Chain a decoder with a fallible transformation.\n").
-spec then(decoder(DLG), fun((DLG) -> {ok, DLI} | {error, binary()})) -> decoder(DLI).
then(Decoder, F) ->
{decoder,
fun(S) ->
gleam@result:'try'(
(erlang:element(2, Decoder))(S),
fun(Value) -> F(Value) end
)
end}.
-file("src/envie/decode.gleam", 74).
?DOC(" Run an additional validation on the decoded value.\n").
-spec validated(decoder(DLM), fun((DLM) -> {ok, DLM} | {error, binary()})) -> decoder(DLM).
validated(Decoder, Validator) ->
{decoder,
fun(S) ->
gleam@result:'try'(
(erlang:element(2, Decoder))(S),
fun(Value) -> Validator(Value) end
)
end}.
-file("src/envie/decode.gleam", 89).
?DOC(" Parse an integer that must be within `[min, max]`.\n").
-spec int_range(integer(), integer()) -> decoder(integer()).
int_range(Min, Max) ->
_pipe = int(),
validated(_pipe, fun(N) -> case (N >= Min) andalso (N =< Max) of
true ->
{ok, N};
false ->
{error,
<<<<<<"Must be between "/utf8,
(erlang:integer_to_binary(Min))/binary>>/binary,
" and "/utf8>>/binary,
(erlang:integer_to_binary(Max))/binary>>}
end end).
-file("src/envie/decode.gleam", 106).
?DOC(" Parse a float that must be within `[min, max]`.\n").
-spec float_range(float(), float()) -> decoder(float()).
float_range(Min, Max) ->
_pipe = float(),
validated(_pipe, fun(N) -> case (N >= Min) andalso (N =< Max) of
true ->
{ok, N};
false ->
{error,
<<<<<<"Must be between "/utf8,
(gleam_stdlib:float_to_string(Min))/binary>>/binary,
" and "/utf8>>/binary,
(gleam_stdlib:float_to_string(Max))/binary>>}
end end).
-file("src/envie/decode.gleam", 124).
?DOC(
" Parse a valid URI. Note that this is very permissive (accepts \"localhost\", etc.).\n"
" Use `url_with_scheme` or `web_url` for stricter validation.\n"
).
-spec url() -> decoder(gleam@uri:uri()).
url() ->
_pipe = string(),
then(_pipe, fun(S) -> _pipe@1 = gleam_stdlib:uri_parse(S),
gleam@result:replace_error(
_pipe@1,
<<"Invalid URL format: "/utf8, S/binary>>
) end).
-file("src/envie/decode.gleam", 133).
?DOC(" Parse a valid URI that must have one of requested schemes (e.g., [\"https\"]).\n").
-spec url_with_scheme(list(binary())) -> decoder(gleam@uri:uri()).
url_with_scheme(Schemes) ->
_pipe = url(),
validated(_pipe, fun(U) -> case erlang:element(2, U) of
{some, S} ->
case gleam@list:contains(Schemes, S) of
true ->
{ok, U};
false ->
{error,
<<<<<<"Invalid scheme: "/utf8, S/binary>>/binary,
". Expected one of: "/utf8>>/binary,
(gleam@string:join(Schemes, <<", "/utf8>>))/binary>>}
end;
none ->
{error,
<<"Missing URL scheme. Expected one of: "/utf8,
(gleam@string:join(Schemes, <<", "/utf8>>))/binary>>}
end end).
-file("src/envie/decode.gleam", 158).
?DOC(" Shortcut for `url_with_scheme([\"http\", \"https\"])`.\n").
-spec web_url() -> decoder(gleam@uri:uri()).
web_url() ->
url_with_scheme([<<"http"/utf8>>, <<"https"/utf8>>]).
-file("src/envie/decode.gleam", 163).
?DOC(" Require that the string starts with a given prefix.\n").
-spec string_prefix(binary()) -> decoder(binary()).
string_prefix(Prefix) ->
_pipe = string(),
validated(
_pipe,
fun(S) -> case gleam_stdlib:string_starts_with(S, Prefix) of
true ->
{ok, S};
false ->
{error,
<<<<"Must start with \""/utf8, Prefix/binary>>/binary,
"\""/utf8>>}
end end
).
-file("src/envie/decode.gleam", 174).
?DOC(" Require a non-empty (after trimming) string.\n").
-spec non_empty_string() -> decoder(binary()).
non_empty_string() ->
_pipe = string(),
validated(_pipe, fun(S) -> case gleam@string:trim(S) of
<<""/utf8>> ->
{error, <<"Must not be empty"/utf8>>};
Trimmed ->
{ok, Trimmed}
end end).
-file("src/envie/decode.gleam", 185).
?DOC(" Split a string by a separator into a list of trimmed, non-empty strings.\n").
-spec string_list(binary()) -> decoder(list(binary())).
string_list(Separator) ->
_pipe = string(),
map(_pipe, fun(S) -> _pipe@1 = S,
_pipe@2 = gleam@string:split(_pipe@1, Separator),
_pipe@3 = gleam@list:map(_pipe@2, fun gleam@string:trim/1),
gleam@list:filter(_pipe@3, fun(X) -> X /= <<""/utf8>> end) end).
-file("src/envie/decode.gleam", 196).
?DOC(" Split a string by a separator and parse each element as an integer.\n").
-spec int_list(binary()) -> decoder(list(integer())).
int_list(Separator) ->
_pipe = string_list(Separator),
then(_pipe, fun(Strings) -> _pipe@1 = Strings,
gleam@list:try_map(
_pipe@1,
fun(S) -> _pipe@2 = gleam_stdlib:parse_int(S),
gleam@result:replace_error(
_pipe@2,
<<"Invalid integer in list: "/utf8, S/binary>>
) end
) end).
-file("src/envie/decode.gleam", 208).
?DOC(" Parse a TCP/UDP port number (1–65 535).\n").
-spec port() -> decoder(integer()).
port() ->
int_range(1, 65535).
-file("src/envie/decode.gleam", 213).
?DOC(" Accept only values that appear in `allowed` (case-sensitive).\n").
-spec one_of(list(binary())) -> decoder(binary()).
one_of(Allowed) ->
{decoder, fun(S) -> case gleam@list:contains(Allowed, S) of
true ->
{ok, S};
false ->
{error,
<<<<<<"Must be one of: "/utf8,
(gleam@string:join(Allowed, <<", "/utf8>>))/binary>>/binary,
"; got: "/utf8>>/binary,
S/binary>>}
end end}.