Current section
Files
Jump to
Current section
Files
src/cowl.erl
-module(cowl).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/cowl.gleam").
-export([secret/1, labeled/2, with_label/2, remove_label/1, get_label/1, mask_with/2, mask/1, to_string/1, equal/2, reveal/1, use_secret/2, map/2, and_then/2, map_label/2, tap/2, from_result/1, from_option/1, labeled_from_option/2, labeled_from_result/2, field_with/2, field/1]).
-export_type([secret/1, strategy/0, peek_mode/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.
-opaque secret(DQR) :: {secret, fun(() -> DQR), gleam@option:option(binary())}.
-type strategy() :: stars |
{fixed, binary()} |
label |
{peek, peek_mode(), binary()} |
{custom, fun((binary()) -> binary())}.
-type peek_mode() :: {first, integer()} |
{last, integer()} |
{both, integer(), integer()}.
-file("src/cowl.gleam", 52).
?DOC(" Wrap `value` as a secret with no label.\n").
-spec secret(DQS) -> secret(DQS).
secret(Value) ->
{secret, fun() -> Value end, none}.
-file("src/cowl.gleam", 57).
?DOC(" Wrap `value` as a secret with a `label`.\n").
-spec labeled(DQU, binary()) -> secret(DQU).
labeled(Value, Label) ->
{secret, fun() -> Value end, {some, Label}}.
-file("src/cowl.gleam", 62).
?DOC(" Set or replace the label on a secret.\n").
-spec with_label(secret(DQW), binary()) -> secret(DQW).
with_label(Secret, Label) ->
{secret, erlang:element(2, Secret), {some, Label}}.
-file("src/cowl.gleam", 67).
?DOC(" Remove the label from a secret. The value is untouched.\n").
-spec remove_label(secret(DQZ)) -> secret(DQZ).
remove_label(Secret) ->
{secret, erlang:element(2, Secret), none}.
-file("src/cowl.gleam", 72).
?DOC(" Return the label, if any.\n").
-spec get_label(secret(any())) -> gleam@option:option(binary()).
get_label(Secret) ->
erlang:element(3, Secret).
-file("src/cowl.gleam", 135).
-spec peek_part(integer(), integer(), binary(), binary(), fun(() -> binary())) -> binary().
peek_part(N, Len, Value, Filler, Build) ->
case N =< 0 of
true ->
Filler;
false ->
case N >= Len of
true ->
Value;
false ->
Build()
end
end.
-file("src/cowl.gleam", 105).
-spec apply_peek(binary(), peek_mode(), binary()) -> binary().
apply_peek(Value, Mode, Filler) ->
Len = string:length(Value),
case Len of
0 ->
Filler;
_ ->
case Mode of
{first, N} ->
peek_part(
N,
Len,
Value,
Filler,
fun() ->
<<(gleam@string:slice(Value, 0, N))/binary,
Filler/binary>>
end
);
{last, N@1} ->
peek_part(
N@1,
Len,
Value,
Filler,
fun() ->
<<Filler/binary,
(gleam@string:slice(Value, Len - N@1, N@1))/binary>>
end
);
{both, N@2, M} ->
case (N@2 =< 0) orelse (M =< 0) of
true ->
Filler;
false ->
case (N@2 + M) >= Len of
true ->
Value;
false ->
<<<<(gleam@string:slice(Value, 0, N@2))/binary,
Filler/binary>>/binary,
(gleam@string:slice(Value, Len - M, M))/binary>>
end
end
end
end.
-file("src/cowl.gleam", 86).
?DOC(" Render a string secret using the given strategy.\n").
-spec mask_with(secret(binary()), strategy()) -> binary().
mask_with(Secret, Strategy) ->
case Strategy of
stars ->
<<"***"/utf8>>;
{fixed, Text} ->
Text;
label ->
case erlang:element(3, Secret) of
{some, L} ->
<<<<"["/utf8, L/binary>>/binary, "]"/utf8>>;
none ->
<<"[secret]"/utf8>>
end;
{peek, Mode, Filler} ->
apply_peek((erlang:element(2, Secret))(), Mode, Filler);
{custom, F} ->
F((erlang:element(2, Secret))())
end.
-file("src/cowl.gleam", 81).
?DOC(" Render a string secret as `\"***\"`.\n").
-spec mask(secret(binary())) -> binary().
mask(Secret) ->
mask_with(Secret, stars).
-file("src/cowl.gleam", 101).
?DOC(" A safe debug string — always `\"Secret(***)\"`, never the actual value.\n").
-spec to_string(secret(binary())) -> binary().
to_string(Secret) ->
<<<<"Secret("/utf8, (mask(Secret))/binary>>/binary, ")"/utf8>>.
-file("src/cowl.gleam", 157).
?DOC(" Compare two secrets by value — labels are ignored.\n").
-spec equal(secret(DRI), secret(DRI)) -> boolean().
equal(A, B) ->
(erlang:element(2, A))() =:= (erlang:element(2, B))().
-file("src/cowl.gleam", 166).
?DOC(" Unwrap and return the raw value.\n").
-spec reveal(secret(DRL)) -> DRL.
reveal(Secret) ->
(erlang:element(2, Secret))().
-file("src/cowl.gleam", 171).
?DOC(" Pass the raw value to `f` without letting it escape the return type.\n").
-spec use_secret(secret(DRN), fun((DRN) -> DRP)) -> DRP.
use_secret(Secret, F) ->
F((erlang:element(2, Secret))()).
-file("src/cowl.gleam", 180).
?DOC(" Transform the wrapped value while keeping it secret. The label is preserved.\n").
-spec map(secret(DRQ), fun((DRQ) -> DRS)) -> secret(DRS).
map(Secret, F) ->
{secret,
fun() -> F((erlang:element(2, Secret))()) end,
erlang:element(3, Secret)}.
-file("src/cowl.gleam", 194).
?DOC(
" Chain a transformation that itself returns a `Secret`, avoiding `Secret(Secret(b))`.\n"
"\n"
" Unlike `map`, `f` returns a `Secret(b)` directly — useful when the\n"
" mapping function produces a secret of its own. The label of the outer\n"
" secret is preserved; the inner secret's label is discarded.\n"
"\n"
" ```gleam\n"
" cowl.secret(\"hunter2\")\n"
" |> cowl.and_then(fn(pw) { hash_password(pw) |> cowl.secret })\n"
" ```\n"
).
-spec and_then(secret(DRU), fun((DRU) -> secret(DRW))) -> secret(DRW).
and_then(Secret, F) ->
Inner = F((erlang:element(2, Secret))()),
{secret, erlang:element(2, Inner), erlang:element(3, Secret)}.
-file("src/cowl.gleam", 200).
?DOC(" Transform only the label, leaving the value untouched.\n").
-spec map_label(secret(DRZ), fun((binary()) -> binary())) -> secret(DRZ).
map_label(Secret, F) ->
{secret, erlang:element(2, Secret), case erlang:element(3, Secret) of
{some, L} ->
{some, F(L)};
none ->
none
end}.
-file("src/cowl.gleam", 211).
?DOC(
" Run `f` for its side effects and return the original secret unchanged.\n"
"\n"
" ⚠️ Never pass a logging function directly — it will print the raw value.\n"
" Use `cowl.to_string` for a safe representation instead.\n"
).
-spec tap(secret(DSC), fun((DSC) -> any())) -> secret(DSC).
tap(Secret, F) ->
_ = F((erlang:element(2, Secret))()),
Secret.
-file("src/cowl.gleam", 221).
?DOC(" Wrap the `Ok` value of a `Result` as a secret, passing errors through.\n").
-spec from_result({ok, DSG} | {error, DSH}) -> {ok, secret(DSG)} | {error, DSH}.
from_result(Res) ->
case Res of
{ok, V} ->
{ok, secret(V)};
{error, E} ->
{error, E}
end.
-file("src/cowl.gleam", 229).
?DOC(" Wrap the `Some` value of an `Option` as a secret, returning `None` unchanged.\n").
-spec from_option(gleam@option:option(DSN)) -> gleam@option:option(secret(DSN)).
from_option(Opt) ->
case Opt of
{some, V} ->
{some, secret(V)};
none ->
none
end.
-file("src/cowl.gleam", 237).
?DOC(" Like `from_option`, but also attaches a label.\n").
-spec labeled_from_option(gleam@option:option(DSR), binary()) -> gleam@option:option(secret(DSR)).
labeled_from_option(Opt, Label) ->
case Opt of
{some, V} ->
{some, labeled(V, Label)};
none ->
none
end.
-file("src/cowl.gleam", 245).
?DOC(" Like `from_result`, but also attaches a label.\n").
-spec labeled_from_result({ok, DSV} | {error, DSW}, binary()) -> {ok,
secret(DSV)} |
{error, DSW}.
labeled_from_result(Res, Label) ->
case Res of
{ok, V} ->
{ok, labeled(V, Label)};
{error, E} ->
{error, E}
end.
-file("src/cowl.gleam", 265).
?DOC(" Like `field`, with an explicit masking strategy.\n").
-spec field_with(secret(binary()), strategy()) -> {binary(), binary()}.
field_with(Secret, Strategy) ->
Key = case erlang:element(3, Secret) of
{some, L} ->
L;
none ->
<<"secret"/utf8>>
end,
{Key, mask_with(Secret, Strategy)}.
-file("src/cowl.gleam", 260).
?DOC(" Return `#(label, \"***\")` for structured logging. Falls back to `\"secret\"` if unlabeled.\n").
-spec field(secret(binary())) -> {binary(), binary()}.
field(Secret) ->
field_with(Secret, stars).