zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

cowboy_constraints.erl (4875B)


      1 %% Copyright (c) 2014-2017, Loïc Hoguin <essen@ninenines.eu>
      2 %%
      3 %% Permission to use, copy, modify, and/or distribute this software for any
      4 %% purpose with or without fee is hereby granted, provided that the above
      5 %% copyright notice and this permission notice appear in all copies.
      6 %%
      7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     14 
     15 -module(cowboy_constraints).
     16 
     17 -export([validate/2]).
     18 -export([reverse/2]).
     19 -export([format_error/1]).
     20 
     21 -type constraint() :: int | nonempty | fun().
     22 -export_type([constraint/0]).
     23 
     24 -type reason() :: {constraint(), any(), any()}.
     25 -export_type([reason/0]).
     26 
     27 -spec validate(binary(), constraint() | [constraint()])
     28 	-> {ok, any()} | {error, reason()}.
     29 validate(Value, Constraints) when is_list(Constraints) ->
     30 	apply_list(forward, Value, Constraints);
     31 validate(Value, Constraint) ->
     32 	apply_list(forward, Value, [Constraint]).
     33 
     34 -spec reverse(any(), constraint() | [constraint()])
     35 	-> {ok, binary()} | {error, reason()}.
     36 reverse(Value, Constraints) when is_list(Constraints) ->
     37 	apply_list(reverse, Value, Constraints);
     38 reverse(Value, Constraint) ->
     39 	apply_list(reverse, Value, [Constraint]).
     40 
     41 -spec format_error(reason()) -> iodata().
     42 format_error({Constraint, Reason, Value}) ->
     43 	apply_constraint(format_error, {Reason, Value}, Constraint).
     44 
     45 apply_list(_, Value, []) ->
     46 	{ok, Value};
     47 apply_list(Type, Value0, [Constraint|Tail]) ->
     48 	case apply_constraint(Type, Value0, Constraint) of
     49 		{ok, Value} ->
     50 			apply_list(Type, Value, Tail);
     51 		{error, Reason} ->
     52 			{error, {Constraint, Reason, Value0}}
     53 	end.
     54 
     55 %% @todo {int, From, To}, etc.
     56 apply_constraint(Type, Value, int) ->
     57 	int(Type, Value);
     58 apply_constraint(Type, Value, nonempty) ->
     59 	nonempty(Type, Value);
     60 apply_constraint(Type, Value, F) when is_function(F) ->
     61 	F(Type, Value).
     62 
     63 %% Constraint functions.
     64 
     65 int(forward, Value) ->
     66 	try
     67 		{ok, binary_to_integer(Value)}
     68 	catch _:_ ->
     69 		{error, not_an_integer}
     70 	end;
     71 int(reverse, Value) ->
     72 	try
     73 		{ok, integer_to_binary(Value)}
     74 	catch _:_ ->
     75 		{error, not_an_integer}
     76 	end;
     77 int(format_error, {not_an_integer, Value}) ->
     78 	io_lib:format("The value ~p is not an integer.", [Value]).
     79 
     80 nonempty(Type, <<>>) when Type =/= format_error ->
     81 	{error, empty};
     82 nonempty(Type, Value) when Type =/= format_error, is_binary(Value) ->
     83 	{ok, Value};
     84 nonempty(format_error, {empty, Value}) ->
     85 	io_lib:format("The value ~p is empty.", [Value]).
     86 
     87 -ifdef(TEST).
     88 
     89 validate_test() ->
     90 	F = fun(_, Value) ->
     91 		try
     92 			{ok, binary_to_atom(Value, latin1)}
     93 		catch _:_ ->
     94 			{error, not_a_binary}
     95 		end
     96 	end,
     97 	%% Value, Constraints, Result.
     98 	Tests = [
     99 		{<<>>, [], <<>>},
    100 		{<<"123">>, int, 123},
    101 		{<<"123">>, [int], 123},
    102 		{<<"123">>, [nonempty, int], 123},
    103 		{<<"123">>, [int, nonempty], 123},
    104 		{<<>>, nonempty, error},
    105 		{<<>>, [nonempty], error},
    106 		{<<"hello">>, F, hello},
    107 		{<<"hello">>, [F], hello},
    108 		{<<"123">>, [F, int], error},
    109 		{<<"123">>, [int, F], error},
    110 		{<<"hello">>, [nonempty, F], hello},
    111 		{<<"hello">>, [F, nonempty], hello}
    112 	],
    113 	[{lists:flatten(io_lib:format("~p, ~p", [V, C])), fun() ->
    114 		case R of
    115 			error -> {error, _} = validate(V, C);
    116 			_ -> {ok, R} = validate(V, C)
    117 		end
    118 	end} || {V, C, R} <- Tests].
    119 
    120 reverse_test() ->
    121 	F = fun(_, Value) ->
    122 		try
    123 			{ok, atom_to_binary(Value, latin1)}
    124 		catch _:_ ->
    125 			{error, not_an_atom}
    126 		end
    127 	end,
    128 	%% Value, Constraints, Result.
    129 	Tests = [
    130 		{<<>>, [], <<>>},
    131 		{123, int, <<"123">>},
    132 		{123, [int], <<"123">>},
    133 		{123, [nonempty, int], <<"123">>},
    134 		{123, [int, nonempty], <<"123">>},
    135 		{<<>>, nonempty, error},
    136 		{<<>>, [nonempty], error},
    137 		{hello, F, <<"hello">>},
    138 		{hello, [F], <<"hello">>},
    139 		{123, [F, int], error},
    140 		{123, [int, F], error},
    141 		{hello, [nonempty, F], <<"hello">>},
    142 		{hello, [F, nonempty], <<"hello">>}
    143 	],
    144 	[{lists:flatten(io_lib:format("~p, ~p", [V, C])), fun() ->
    145 		case R of
    146 			error -> {error, _} = reverse(V, C);
    147 			_ -> {ok, R} = reverse(V, C)
    148 		end
    149 	end} || {V, C, R} <- Tests].
    150 
    151 int_format_error_test() ->
    152 	{error, Reason} = validate(<<"string">>, int),
    153 	Bin = iolist_to_binary(format_error(Reason)),
    154 	true = is_binary(Bin),
    155 	ok.
    156 
    157 nonempty_format_error_test() ->
    158 	{error, Reason} = validate(<<>>, nonempty),
    159 	Bin = iolist_to_binary(format_error(Reason)),
    160 	true = is_binary(Bin),
    161 	ok.
    162 
    163 fun_format_error_test() ->
    164 	F = fun
    165 		(format_error, {test, <<"value">>}) ->
    166 			formatted;
    167 		(_, _) ->
    168 			{error, test}
    169 	end,
    170 	{error, Reason} = validate(<<"value">>, F),
    171 	formatted = format_error(Reason),
    172 	ok.
    173 
    174 -endif.