0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-15 12:40:25 +00:00

Add the 'int' constraint

This commit is contained in:
Loïc Hoguin 2013-01-28 19:56:42 +01:00
parent a357c49d1b
commit a5a69353f1
2 changed files with 59 additions and 10 deletions

View file

@ -196,8 +196,9 @@ Constraints
----------- -----------
After the matching has completed, the resulting bindings can be tested After the matching has completed, the resulting bindings can be tested
against a set of constraints. The match will succeed only if they all against a set of constraints. Constraints are only tested when the
succeed. binding is defined. They run in the order you defined them. The match
will succeed only if they all succeed.
They are always given as a two or three elements tuple, where the first They are always given as a two or three elements tuple, where the first
element is the name of the binding, the second element is the constraint's element is the name of the binding, the second element is the constraint's

View file

@ -33,7 +33,7 @@
-export_type([bindings/0]). -export_type([bindings/0]).
-export_type([tokens/0]). -export_type([tokens/0]).
-type constraints() :: []. -type constraints() :: [{atom(), int}].
-export_type([constraints/0]). -export_type([constraints/0]).
-type route_match() :: binary() | string(). -type route_match() :: binary() | string().
@ -222,16 +222,22 @@ match([], _, _) ->
%% If the host is '_' then there can be no constraints. %% If the host is '_' then there can be no constraints.
match([{'_', [], PathMatchs}|_Tail], _, Path) -> match([{'_', [], PathMatchs}|_Tail], _, Path) ->
match_path(PathMatchs, undefined, Path, []); match_path(PathMatchs, undefined, Path, []);
match([{HostMatch, _Constraints, PathMatchs}|Tail], Tokens, Path) match([{HostMatch, Constraints, PathMatchs}|Tail], Tokens, Path)
when is_list(Tokens) -> when is_list(Tokens) ->
case list_match(Tokens, HostMatch, []) of case list_match(Tokens, HostMatch, []) of
false -> false ->
match(Tail, Tokens, Path); match(Tail, Tokens, Path);
{true, Bindings, undefined} ->
match_path(PathMatchs, undefined, Path, Bindings);
{true, Bindings, HostInfo} -> {true, Bindings, HostInfo} ->
match_path(PathMatchs, lists:reverse(HostInfo), HostInfo2 = case HostInfo of
Path, Bindings) undefined -> undefined;
_ -> lists:reverse(HostInfo)
end,
case check_constraints(Constraints, Bindings) of
{ok, Bindings2} ->
match_path(PathMatchs, HostInfo2, Path, Bindings2);
nomatch ->
match(Tail, Tokens, Path)
end
end; end;
match(Dispatch, Host, Path) -> match(Dispatch, Host, Path) ->
match(Dispatch, split_host(Host), Path). match(Dispatch, split_host(Host), Path).
@ -249,19 +255,50 @@ match_path([{'_', [], Handler, Opts}|_Tail], HostInfo, _, Bindings) ->
{ok, Handler, Opts, Bindings, HostInfo, undefined}; {ok, Handler, Opts, Bindings, HostInfo, undefined};
match_path([{<<"*">>, _Constraints, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) -> match_path([{<<"*">>, _Constraints, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) ->
{ok, Handler, Opts, Bindings, HostInfo, undefined}; {ok, Handler, Opts, Bindings, HostInfo, undefined};
match_path([{PathMatch, _Constraints, Handler, Opts}|Tail], HostInfo, Tokens, match_path([{PathMatch, Constraints, Handler, Opts}|Tail], HostInfo, Tokens,
Bindings) when is_list(Tokens) -> Bindings) when is_list(Tokens) ->
case list_match(Tokens, PathMatch, []) of case list_match(Tokens, PathMatch, []) of
false -> false ->
match_path(Tail, HostInfo, Tokens, Bindings); match_path(Tail, HostInfo, Tokens, Bindings);
{true, PathBinds, PathInfo} -> {true, PathBinds, PathInfo} ->
{ok, Handler, Opts, Bindings ++ PathBinds, HostInfo, PathInfo} case check_constraints(Constraints, PathBinds) of
{ok, PathBinds2} ->
{ok, Handler, Opts, Bindings ++ PathBinds2,
HostInfo, PathInfo};
nomatch ->
match_path(Tail, HostInfo, Tokens, Bindings)
end
end; end;
match_path(_Dispatch, _HostInfo, badrequest, _Bindings) -> match_path(_Dispatch, _HostInfo, badrequest, _Bindings) ->
{error, badrequest, path}; {error, badrequest, path};
match_path(Dispatch, HostInfo, Path, Bindings) -> match_path(Dispatch, HostInfo, Path, Bindings) ->
match_path(Dispatch, HostInfo, split_path(Path), Bindings). match_path(Dispatch, HostInfo, split_path(Path), Bindings).
check_constraints([], Bindings) ->
{ok, Bindings};
check_constraints([Constraint|Tail], Bindings) ->
Name = element(1, Constraint),
case lists:keyfind(Name, 1, Bindings) of
false ->
check_constraints(Tail, Bindings);
{_, Value} ->
case check_constraint(Constraint, Value) of
true ->
check_constraints(Tail, Bindings);
{true, Value2} ->
Bindings2 = lists:keyreplace(Name, 1, Bindings,
{Name, Value2}),
check_constraints(Tail, Bindings2);
false ->
nomatch
end
end.
check_constraint({_, int}, Value) ->
try {true, list_to_integer(binary_to_list(Value))}
catch _:_ -> false
end.
%% @doc Split a hostname into a list of tokens. %% @doc Split a hostname into a list of tokens.
-spec split_host(binary()) -> tokens(). -spec split_host(binary()) -> tokens().
split_host(Host) -> split_host(Host) ->
@ -478,4 +515,15 @@ match_info_test_() ->
R = match(Dispatch, H, P) R = match(Dispatch, H, P)
end} || {H, P, R} <- Tests]. end} || {H, P, R} <- Tests].
match_constraints_test() ->
Dispatch = [{'_', [],
[{[<<"path">>, value], [{value, int}], match, []}]}],
{ok, _, [], [{value, 123}], _, _} = match(Dispatch,
<<"ninenines.eu">>, <<"/path/123">>),
{ok, _, [], [{value, 123}], _, _} = match(Dispatch,
<<"ninenines.eu">>, <<"/path/123/">>),
{error, notfound, path} = match(Dispatch,
<<"ninenines.eu">>, <<"/path/NaN/">>),
ok.
-endif. -endif.