Current section
Files
Jump to
Current section
Files
src/howdy@server.erl
-module(howdy@server).
-compile(no_auto_import).
-export([start/1, start_with_config/2, start_with_port/3, get_routes/1, register_routes/2, register_middleware/2]).
-export_type([message/1, state/1, route/1, request_type/1]).
-opaque message(IJQ) :: {add_route,
route(IJQ),
gleam@otp@process:sender(binary())} |
{get_routes, gleam@otp@process:sender(binary())} |
{match_route,
binary(),
gleam@http:method(),
gleam@otp@process:sender(request_type(IJQ))} |
{register_middleware,
fun((fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))),
gleam@otp@process:sender(boolean())} |
{get_middleware,
gleam@otp@process:sender(gleam@option:option(fun((fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder())))))}.
-type state(IJR) :: {state,
list(route(IJR)),
gleam@option:option(fun((fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder())))),
IJR}.
-opaque route(IJS) :: {route,
gleam@http:method(),
binary(),
fun((howdy@context:context(IJS)) -> gleam@http@response:response(gleam@bit_builder:bit_builder())),
list(fun((fun((howdy@context:context(IJS)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((howdy@context:context(IJS)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))))}.
-opaque request_type(IJT) :: non_routable_request |
{successful_request, route(IJT), list(howdy@url_parser:url_segment()), IJT}.
-spec start(howdy@router:route(nil)) -> {ok,
gleam@otp@process:sender(message(any()))} |
{error, glisten:start_error()}.
start(Router) ->
start_with_port(3000, Router, nil).
-spec start_with_config(howdy@router:route(IJW), IJW) -> {ok,
gleam@otp@process:sender(message(any()))} |
{error, glisten:start_error()}.
start_with_config(Router, Config) ->
start_with_port(3000, Router, Config).
-spec start_with_port(integer(), howdy@router:route(IJZ), IJZ) -> {ok,
gleam@otp@process:sender(message(IJZ))} |
{error, glisten:start_error()}.
start_with_port(Port, Router, Config) ->
Actor = gleam@otp@actor:start(
{state, [], none, Config},
fun handle_message/2
),
case Actor of
{ok, Pid} ->
case mist:serve(
Port,
mist@http:handler(
fun(_capture) -> my_service(Pid, _capture) end
)
) of
{ok, _@1} ->
register_routes(Pid, Router),
{ok, Pid};
{error, Error} ->
{error, Error}
end;
{error, _@2} ->
{error,
{acceptor_failed,
{abnormal,
gleam@dynamic:from(<<"Howby OTP service failed to start"/utf8>>)}}}
end.
-spec not_found() -> gleam@http@response:response(gleam@bit_builder:bit_builder()).
not_found() ->
Body = gleam@bit_builder:from_string(<<"Not Found!"/utf8>>),
_pipe = gleam@http@response:new(404),
_pipe@1 = gleam@http@response:prepend_header(
_pipe,
<<"made-with"/utf8>>,
<<"Gleam"/utf8>>
),
gleam@http@response:set_body(_pipe@1, Body).
-spec my_service(
gleam@otp@process:sender(message(any())),
gleam@http@request:request(bitstring())
) -> gleam@http@response:response(gleam@bit_builder:bit_builder()).
my_service(Pid, Req) ->
Service = fun(Request) ->
Match = match_route(
Pid,
erlang:element(8, Request),
erlang:element(2, Request)
),
case Match of
{successful_request, Route, Url, Config} ->
Context = howdy@context:new(Url, Request, Config),
(add_filters_to_response(
erlang:element(5, Route),
erlang:element(4, Route)
))(Context);
non_routable_request ->
not_found()
end
end,
case get_middleware(Pid) of
{some, Middleware} ->
(Middleware(Service))(Req);
none ->
Service(Req)
end.
-spec add_filters_to_response(
list(fun((fun((howdy@context:context(IKG)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((howdy@context:context(IKG)) -> gleam@http@response:response(gleam@bit_builder:bit_builder())))),
fun((howdy@context:context(IKG)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))
) -> fun((howdy@context:context(IKG)) -> gleam@http@response:response(gleam@bit_builder:bit_builder())).
add_filters_to_response(Filters, Fun) ->
case gleam@list:length(Filters) of
0 ->
Fun;
_@1 ->
case gleam@list:reduce(
gleam@list:reverse(Filters),
fun(Filter, Next) -> gleam@function:compose(Filter, Next) end
) of
{ok, Fillters_compose} ->
Fillters_compose(Fun);
{error, _@2} ->
Fun
end
end.
-spec process_map(
gleam@otp@process:sender(message(IKP)),
binary(),
fun((howdy@context:context(IKP)) -> gleam@http@response:response(gleam@bit_builder:bit_builder())),
gleam@http:method(),
list(fun((fun((howdy@context:context(IKP)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((howdy@context:context(IKP)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))))
) -> gleam@otp@process:sender(message(IKP)).
process_map(Pid, Url, Func, Method, Filters) ->
gleam@otp@process:call(
Pid,
fun(_capture) ->
{add_route, {route, Method, Url, Func, Filters}, _capture}
end,
100
),
Pid.
-spec get_routes(gleam@otp@process:sender(message(IQT))) -> gleam@otp@process:sender(message(IQT)).
get_routes(Pid) ->
Response = gleam@otp@process:call(Pid, fun(A) -> {get_routes, A} end, 100),
gleam@io:println(Response),
Pid.
-spec register_routes(
gleam@otp@process:sender(message(any())),
howdy@router:route(any())
) -> gleam@otp@process:sender(message(any())).
register_routes(Pid, Route) ->
register_routes_with_root(Pid, Route, <<""/utf8>>, []),
Pid.
-spec register_middleware(
gleam@otp@process:sender(message(IRB)),
fun((fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder())))
) -> gleam@otp@process:sender(message(IRB)).
register_middleware(Pid, Middleware) ->
gleam@otp@process:call(
Pid,
fun(_capture) -> {register_middleware, Middleware, _capture} end,
100
),
Pid.
-spec register_routes_with_root(
gleam@otp@process:sender(message(ILJ)),
howdy@router:route(ILJ),
binary(),
list(fun((fun((howdy@context:context(ILJ)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((howdy@context:context(ILJ)) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))))
) -> nil.
register_routes_with_root(Pid, Routes, Root, Filters) ->
case Routes of
{get, Route, Func} ->
process_map(Pid, join_urls(Root, Route), Func, get, Filters),
nil;
{post, Route@1, Func@1} ->
process_map(Pid, join_urls(Root, Route@1), Func@1, post, Filters),
nil;
{delete, Route@2, Func@2} ->
process_map(Pid, join_urls(Root, Route@2), Func@2, delete, Filters),
nil;
{put, Route@3, Func@3} ->
process_map(Pid, join_urls(Root, Route@3), Func@3, put, Filters),
nil;
{patch, Route@4, Func@4} ->
process_map(Pid, join_urls(Root, Route@4), Func@4, patch, Filters),
nil;
{custom, Method, Route@5, Func@5} ->
process_map(
Pid,
join_urls(Root, Route@5),
Func@5,
{other, Method},
Filters
),
nil;
{router_map, Route_path, Route_list} ->
_pipe = Route_list,
gleam@list:each(
_pipe,
fun(Route@6) ->
register_routes_with_root(
Pid,
Route@6,
join_urls(Root, Route_path),
Filters
)
end
);
{router_map_with_filters, Route_path@1, Route_list@1, Filter_list} ->
_pipe@1 = Route_list@1,
gleam@list:each(
_pipe@1,
fun(Route@7) ->
register_routes_with_root(
Pid,
Route@7,
join_urls(Root, Route_path@1),
gleam@list:append(Filters, Filter_list)
)
end
);
{static, Route_path@2, File_path} ->
Full_route = case gleam@string:ends_with(
Route_path@2,
<<"/*"/utf8>>
) of
true ->
join_urls(Root, Route_path@2);
false ->
join_urls(Root, join_urls(Route_path@2, <<"/*"/utf8>>))
end,
process_map(
Pid,
Full_route,
howdy@static_resource:get_file_contents(File_path),
get,
Filters
),
nil;
{spa, Route_path@3, Spa_file_path} ->
Full_route@1 = case gleam@string:ends_with(
Route_path@3,
<<"/*"/utf8>>
) of
true ->
join_urls(Root, Route_path@3);
false ->
join_urls(Root, join_urls(Route_path@3, <<"/*"/utf8>>))
end,
process_map(
Pid,
Full_route@1,
howdy@static_resource:get_spa_file_contents(Spa_file_path),
get,
Filters
),
nil
end.
-spec join_urls(binary(), binary()) -> binary().
join_urls(Lhs, Rhs) ->
case {gleam@string:ends_with(Lhs, <<"/"/utf8>>),
gleam@string:starts_with(Rhs, <<"/"/utf8>>)} of
{true, true} ->
gleam@string:append(Lhs, gleam@string:drop_left(Rhs, 1));
{true, false} ->
gleam@string:append(Lhs, Rhs);
{false, true} ->
gleam@string:append(Lhs, Rhs);
{false, false} ->
_pipe = Lhs,
_pipe@1 = gleam@string:append(_pipe, <<"/"/utf8>>),
gleam@string:append(_pipe@1, Rhs)
end.
-spec match_route(
gleam@otp@process:sender(message(ILT)),
binary(),
gleam@http:method()
) -> request_type(ILT).
match_route(Pid, Path, Method) ->
gleam@otp@process:call(
Pid,
fun(_capture) -> {match_route, Path, Method, _capture} end,
100
).
-spec get_middleware(gleam@otp@process:sender(message(any()))) -> gleam@option:option(fun((fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))) -> fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder())))).
get_middleware(Pid) ->
gleam@otp@process:call(Pid, fun(A) -> {get_middleware, A} end, 100).
-spec handle_message(message(IMB), state(IMB)) -> gleam@otp@actor:next(state(IMB)).
handle_message(Msg, State) ->
case Msg of
{add_route, Route, Reply_channel} ->
gleam@otp@process:send(Reply_channel, <<"Adding route"/utf8>>),
New_state = erlang:setelement(
2,
State,
gleam@list:append(erlang:element(2, State), [Route])
),
{continue, New_state};
{get_routes, Reply_channel@1} ->
print_route(erlang:element(2, State)),
gleam@otp@process:send(Reply_channel@1, <<"Getting routes"/utf8>>),
{continue, State};
{match_route, Path, Method, Reply_channel@2} ->
Result = find_route(
erlang:element(2, State),
Path,
Method,
erlang:element(4, State)
),
gleam@otp@process:send(Reply_channel@2, Result),
{continue, State};
{register_middleware, Middleware, Reply_channel@3} ->
New_state@1 = erlang:setelement(3, State, {some, Middleware}),
gleam@otp@process:send(Reply_channel@3, true),
{continue, New_state@1};
{get_middleware, Reply_channel@4} ->
gleam@otp@process:send(Reply_channel@4, erlang:element(3, State)),
{continue, State}
end.
-spec print_route(list(route(any()))) -> nil.
print_route(Routes) ->
case Routes of
[] ->
gleam@io:println(<<"No more routes"/utf8>>);
[Head | Tail] ->
gleam@io:println(erlang:element(3, Head)),
print_route(Tail)
end.
-spec find_route(list(route(IMK)), binary(), gleam@http:method(), IMK) -> request_type(IMK).
find_route(Routes, Path, Method, Config) ->
_pipe = Routes,
gleam@list:fold_until(
_pipe,
non_routable_request,
fun(Acc, Route) ->
Url = howdy@url_parser:parse(erlang:element(3, Route), Path),
case {Url, erlang:element(2, Route) =:= Method} of
{{ok, Segments}, true} ->
{stop, {successful_request, Route, Segments, Config}};
{_@1, _@2} ->
{continue, Acc}
end
end
).