Current section
Files
Jump to
Current section
Files
src/mochi@context.erl
-module(mochi@context).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/mochi/context.gleam").
-export([request_info/3, get_header/2, get_authorization/1, get_bearer_token/1, new_pipeline/0, add_builder/2, build_context/3, try_build_context/3, to_dynamic/1, add_to_context/2, add_to_context_or/3, require/1, transform/1, bearer_token_builder/0, require_bearer_token/0, request_metadata_builder/0, headers_builder/0]).
-export_type([request_info/0, context_pipeline/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 request_info() :: {request_info,
gleam@dict:dict(binary(), binary()),
binary(),
binary()}.
-opaque context_pipeline() :: {context_pipeline,
list(fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}))}.
-file("src/mochi/context.gleam", 43).
?DOC(" Create a new RequestInfo\n").
-spec request_info(gleam@dict:dict(binary(), binary()), binary(), binary()) -> request_info().
request_info(Headers, Method, Path) ->
{request_info, Headers, Method, Path}.
-file("src/mochi/context.gleam", 52).
?DOC(" Get a header value from request info (case-insensitive)\n").
-spec get_header(request_info(), binary()) -> {ok, binary()} | {error, nil}.
get_header(Info, Name) ->
Lower_name = string:lowercase(Name),
_pipe = maps:to_list(erlang:element(2, Info)),
gleam@list:find_map(
_pipe,
fun(Pair) ->
{K, V} = Pair,
case string:lowercase(K) =:= Lower_name of
true ->
{ok, V};
false ->
{error, nil}
end
end
).
-file("src/mochi/context.gleam", 65).
?DOC(" Get the Authorization header value\n").
-spec get_authorization(request_info()) -> {ok, binary()} | {error, nil}.
get_authorization(Info) ->
get_header(Info, <<"authorization"/utf8>>).
-file("src/mochi/context.gleam", 70).
?DOC(" Get bearer token from Authorization header\n").
-spec get_bearer_token(request_info()) -> {ok, binary()} | {error, nil}.
get_bearer_token(Info) ->
case get_authorization(Info) of
{ok, Auth} ->
case Auth of
<<"Bearer "/utf8, Token/binary>> ->
{ok, Token};
<<"bearer "/utf8, Token@1/binary>> ->
{ok, Token@1};
_ ->
{error, nil}
end;
{error, _} ->
{error, nil}
end.
-file("src/mochi/context.gleam", 88).
?DOC(" Create a new empty context pipeline\n").
-spec new_pipeline() -> context_pipeline().
new_pipeline() ->
{context_pipeline, []}.
-file("src/mochi/context.gleam", 94).
?DOC(
" Add a context builder to the pipeline\n"
" Builders execute in the order they are added\n"
).
-spec add_builder(
context_pipeline(),
fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()})
) -> context_pipeline().
add_builder(Pipeline, Builder) ->
{context_pipeline, lists:append(erlang:element(2, Pipeline), [Builder])}.
-file("src/mochi/context.gleam", 103).
?DOC(
" Build the execution context by running all builders in sequence\n"
" Each builder receives the output of the previous builder\n"
).
-spec build_context(
context_pipeline(),
request_info(),
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())
) -> {ok, gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}.
build_context(Pipeline, Request, Initial) ->
gleam@list:fold(
erlang:element(2, Pipeline),
{ok, Initial},
fun(Acc, Builder) -> case Acc of
{ok, Ctx} ->
Builder(Request, Ctx);
{error, E} ->
{error, E}
end end
).
-file("src/mochi/context.gleam", 117).
?DOC(" Try to build context, returning initial context on any error\n").
-spec try_build_context(
context_pipeline(),
request_info(),
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())
) -> gleam@dict:dict(binary(), gleam@dynamic:dynamic_()).
try_build_context(Pipeline, Request, Initial) ->
_pipe = build_context(Pipeline, Request, Initial),
gleam@result:unwrap(_pipe, Initial).
-file("src/mochi/context.gleam", 127).
?DOC(" Convert context dict to Dynamic for use with ExecutionContext\n").
-spec to_dynamic(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> gleam@dynamic:dynamic_().
to_dynamic(Ctx) ->
gleam_stdlib:identity(Ctx).
-file("src/mochi/context.gleam", 136).
?DOC(" Create a builder that adds a value to the context dict\n").
-spec add_to_context(
binary(),
fun((request_info()) -> {ok, gleam@dynamic:dynamic_()} | {error, binary()})
) -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
add_to_context(Key, Extractor) ->
fun(Request, Ctx) -> case Extractor(Request) of
{ok, Value} ->
{ok, gleam@dict:insert(Ctx, Key, Value)};
{error, E} ->
{error, E}
end end.
-file("src/mochi/context.gleam", 149).
?DOC(" Create a builder that adds a value to context, with a fallback on error\n").
-spec add_to_context_or(
binary(),
fun((request_info()) -> {ok, gleam@dynamic:dynamic_()} | {error, binary()}),
gleam@dynamic:dynamic_()
) -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
add_to_context_or(Key, Extractor, Default) ->
fun(Request, Ctx) ->
Value = case Extractor(Request) of
{ok, V} ->
V;
{error, _} ->
Default
end,
{ok, gleam@dict:insert(Ctx, Key, Value)}
end.
-file("src/mochi/context.gleam", 164).
?DOC(" Create a builder that validates a condition or fails\n").
-spec require(
fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
nil} |
{error, binary()})
) -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
require(Validator) ->
fun(Request, Ctx) -> case Validator(Request, Ctx) of
{ok, _} ->
{ok, Ctx};
{error, E} ->
{error, E}
end end.
-file("src/mochi/context.gleam", 176).
?DOC(" Create a builder that transforms the entire context\n").
-spec transform(
fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()})
) -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
transform(Transformer) ->
Transformer.
-file("src/mochi/context.gleam", 188).
?DOC(" Builder that extracts the bearer token and adds it to context as \"token\"\n").
-spec bearer_token_builder() -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
bearer_token_builder() ->
add_to_context_or(
<<"token"/utf8>>,
fun(Request) -> _pipe = get_bearer_token(Request),
_pipe@1 = gleam@result:map(_pipe, fun gleam_stdlib:identity/1),
gleam@result:map_error(
_pipe@1,
fun(_) -> <<"No bearer token found"/utf8>> end
) end,
gleam_stdlib:identity(nil)
).
-file("src/mochi/context.gleam", 201).
?DOC(" Builder that requires a bearer token to be present\n").
-spec require_bearer_token() -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
require_bearer_token() ->
require(fun(Request, _) -> case get_bearer_token(Request) of
{ok, _} ->
{ok, nil};
{error, _} ->
{error,
<<"Authorization required: Bearer token missing"/utf8>>}
end end).
-file("src/mochi/context.gleam", 211).
?DOC(" Builder that adds request metadata to context\n").
-spec request_metadata_builder() -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
request_metadata_builder() ->
fun(Request, Ctx) ->
Metadata = maps:from_list(
[{<<"method"/utf8>>,
gleam_stdlib:identity(erlang:element(3, Request))},
{<<"path"/utf8>>,
gleam_stdlib:identity(erlang:element(4, Request))}]
),
{ok,
gleam@dict:insert(
Ctx,
<<"request"/utf8>>,
gleam_stdlib:identity(Metadata)
)}
end.
-file("src/mochi/context.gleam", 223).
?DOC(" Builder that adds all headers to context under \"headers\" key\n").
-spec headers_builder() -> fun((request_info(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} |
{error, binary()}).
headers_builder() ->
fun(Request, Ctx) ->
Headers_dynamic = gleam@dict:map_values(
erlang:element(2, Request),
fun(_, V) -> gleam_stdlib:identity(V) end
),
{ok,
gleam@dict:insert(
Ctx,
<<"headers"/utf8>>,
gleam_stdlib:identity(Headers_dynamic)
)}
end.