diff options
author | Loïc Hoguin <[email protected]> | 2019-12-18 21:27:35 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2019-12-18 21:27:35 +0100 |
commit | ea6b482f82e016aeb171c3fa37734a97a182f63f (patch) | |
tree | ba801d030e86da2ee6ef05b57a87d1107ccec49e /src/cow_uri_templates.erl | |
parent | 34291d5bb42c03d92522dfa18d38aa0d22bcb8ec (diff) | |
download | cowlib-ea6b482f82e016aeb171c3fa37734a97a182f63f.tar.gz cowlib-ea6b482f82e016aeb171c3fa37734a97a182f63f.tar.bz2 cowlib-ea6b482f82e016aeb171c3fa37734a97a182f63f.zip |
Rename cow_uri_templates to cow_uri_template
Fits better since we are dealing with a single template at a time.
Diffstat (limited to 'src/cow_uri_templates.erl')
-rw-r--r-- | src/cow_uri_templates.erl | 356 |
1 files changed, 0 insertions, 356 deletions
diff --git a/src/cow_uri_templates.erl b/src/cow_uri_templates.erl deleted file mode 100644 index 32ca07c..0000000 --- a/src/cow_uri_templates.erl +++ /dev/null @@ -1,356 +0,0 @@ -%% Copyright (c) 2019, Loïc Hoguin <[email protected]> -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -%% This is a full level 4 implementation of URI Templates -%% as defined by RFC6570. - --module(cow_uri_templates). - --export([parse/1]). --export([expand/2]). - --type op() :: simple_string_expansion - | reserved_expansion - | fragment_expansion - | label_expansion_with_dot_prefix - | path_segment_expansion - | path_style_parameter_expansion - | form_style_query_expansion - | form_style_query_continuation. - --type var_list() :: [ - {no_modifier, binary()} - | {{prefix_modifier, pos_integer()}, binary()} - | {explode_modifier, binary()} -]. - --type uri_template() :: [ - binary() | {expr, op(), var_list()} -]. --export_type([uri_template/0]). - --type variables() :: #{ - binary() => binary() - | integer() - | float() - | [binary()] - | #{binary() => binary()} -}. - --include("cow_inline.hrl"). --include("cow_parse.hrl"). - -%% Parse a URI template. - --spec parse(binary()) -> uri_template(). -parse(URITemplate) -> - parse(URITemplate, <<>>). - -parse(<<>>, <<>>) -> - []; -parse(<<>>, Acc) -> - [Acc]; -parse(<<${,R/bits>>, <<>>) -> - parse_expr(R); -parse(<<${,R/bits>>, Acc) -> - [Acc|parse_expr(R)]; -%% @todo Probably should reject unallowed characters so that -%% we don't produce invalid URIs. -parse(<<C,R/bits>>, Acc) when C =/= $} -> - parse(R, <<Acc/binary, C>>). - -parse_expr(<<$+,R/bits>>) -> - parse_var_list(R, reserved_expansion, []); -parse_expr(<<$#,R/bits>>) -> - parse_var_list(R, fragment_expansion, []); -parse_expr(<<$.,R/bits>>) -> - parse_var_list(R, label_expansion_with_dot_prefix, []); -parse_expr(<<$/,R/bits>>) -> - parse_var_list(R, path_segment_expansion, []); -parse_expr(<<$;,R/bits>>) -> - parse_var_list(R, path_style_parameter_expansion, []); -parse_expr(<<$?,R/bits>>) -> - parse_var_list(R, form_style_query_expansion, []); -parse_expr(<<$&,R/bits>>) -> - parse_var_list(R, form_style_query_continuation, []); -parse_expr(R) -> - parse_var_list(R, simple_string_expansion, []). - -parse_var_list(<<C,R/bits>>, Op, List) - when ?IS_ALPHANUM(C) or (C =:= $_) -> - parse_varname(R, Op, List, <<C>>). - -parse_varname(<<C,R/bits>>, Op, List, Name) - when ?IS_ALPHANUM(C) or (C =:= $_) or (C =:= $.) or (C =:= $%) -> - parse_varname(R, Op, List, <<Name/binary,C>>); -parse_varname(<<$:,C,R/bits>>, Op, List, Name) - when (C =:= $1) or (C =:= $2) or (C =:= $3) or (C =:= $4) or (C =:= $5) - or (C =:= $6) or (C =:= $7) or (C =:= $8) or (C =:= $9) -> - parse_prefix_modifier(R, Op, List, Name, <<C>>); -parse_varname(<<$*,$,,R/bits>>, Op, List, Name) -> - parse_var_list(R, Op, [{explode_modifier, Name}|List]); -parse_varname(<<$*,$},R/bits>>, Op, List, Name) -> - [{expr, Op, lists:reverse([{explode_modifier, Name}|List])}|parse(R, <<>>)]; -parse_varname(<<$,,R/bits>>, Op, List, Name) -> - parse_var_list(R, Op, [{no_modifier, Name}|List]); -parse_varname(<<$},R/bits>>, Op, List, Name) -> - [{expr, Op, lists:reverse([{no_modifier, Name}|List])}|parse(R, <<>>)]. - -parse_prefix_modifier(<<C,R/bits>>, Op, List, Name, Acc) - when ?IS_DIGIT(C), byte_size(Acc) < 4 -> - parse_prefix_modifier(R, Op, List, Name, <<Acc/binary,C>>); -parse_prefix_modifier(<<$,,R/bits>>, Op, List, Name, Acc) -> - parse_var_list(R, Op, [{{prefix_modifier, binary_to_integer(Acc)}, Name}|List]); -parse_prefix_modifier(<<$},R/bits>>, Op, List, Name, Acc) -> - [{expr, Op, lists:reverse([{{prefix_modifier, binary_to_integer(Acc)}, Name}|List])}|parse(R, <<>>)]. - -%% Expand a URI template (after parsing it if necessary). - --spec expand(binary() | uri_template(), variables()) -> iodata(). -expand(URITemplate, Vars) when is_binary(URITemplate) -> - expand(parse(URITemplate), Vars); -expand(URITemplate, Vars) -> - expand1(URITemplate, Vars). - -expand1([], _) -> - []; -expand1([Literal|Tail], Vars) when is_binary(Literal) -> - [Literal|expand1(Tail, Vars)]; -expand1([{expr, simple_string_expansion, VarList}|Tail], Vars) -> - [simple_string_expansion(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, reserved_expansion, VarList}|Tail], Vars) -> - [reserved_expansion(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, fragment_expansion, VarList}|Tail], Vars) -> - [fragment_expansion(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, label_expansion_with_dot_prefix, VarList}|Tail], Vars) -> - [label_expansion_with_dot_prefix(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, path_segment_expansion, VarList}|Tail], Vars) -> - [path_segment_expansion(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, path_style_parameter_expansion, VarList}|Tail], Vars) -> - [path_style_parameter_expansion(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, form_style_query_expansion, VarList}|Tail], Vars) -> - [form_style_query_expansion(VarList, Vars)|expand1(Tail, Vars)]; -expand1([{expr, form_style_query_continuation, VarList}|Tail], Vars) -> - [form_style_query_continuation(VarList, Vars)|expand1(Tail, Vars)]. - -simple_string_expansion(VarList, Vars) -> - lists:join($,, [ - apply_modifier(Modifier, unreserved, $,, Value) - || {Modifier, _Name, Value} <- lookup_variables(VarList, Vars)]). - -reserved_expansion(VarList, Vars) -> - lists:join($,, [ - apply_modifier(Modifier, reserved, $,, Value) - || {Modifier, _Name, Value} <- lookup_variables(VarList, Vars)]). - -fragment_expansion(VarList, Vars) -> - case reserved_expansion(VarList, Vars) of - [] -> []; - Expanded -> [$#, Expanded] - end. - -label_expansion_with_dot_prefix(VarList, Vars) -> - segment_expansion(VarList, Vars, $.). - -path_segment_expansion(VarList, Vars) -> - segment_expansion(VarList, Vars, $/). - -segment_expansion(VarList, Vars, Sep) -> - Expanded = lists:join(Sep, [ - apply_modifier(Modifier, unreserved, Sep, Value) - || {Modifier, _Name, Value} <- lookup_variables(VarList, Vars)]), - case Expanded of - [] -> []; - [[]] -> []; - _ -> [Sep, Expanded] - end. - -path_style_parameter_expansion(VarList, Vars) -> - parameter_expansion(VarList, Vars, $;, $;, trim). - -form_style_query_expansion(VarList, Vars) -> - parameter_expansion(VarList, Vars, $?, $&, no_trim). - -form_style_query_continuation(VarList, Vars) -> - parameter_expansion(VarList, Vars, $&, $&, no_trim). - -parameter_expansion(VarList, Vars, LeadingSep, Sep, Trim) -> - Expanded = lists:join(Sep, [ - apply_parameter_modifier(Modifier, unreserved, Sep, Trim, Name, Value) - || {Modifier, Name, Value} <- lookup_variables(VarList, Vars)]), - case Expanded of - [] -> []; - [[]] -> []; - _ -> [LeadingSep, Expanded] - end. - -lookup_variables([], _) -> - []; -lookup_variables([{Modifier, Name}|Tail], Vars) -> - case Vars of - #{Name := Value} -> [{Modifier, Name, Value}|lookup_variables(Tail, Vars)]; - _ -> lookup_variables(Tail, Vars) - end. - -apply_modifier(no_modifier, AllowedChars, _, List) when is_list(List) -> - lists:join($,, [urlencode(Value, AllowedChars) || Value <- List]); -apply_modifier(explode_modifier, AllowedChars, ExplodeSep, List) when is_list(List) -> - lists:join(ExplodeSep, [urlencode(Value, AllowedChars) || Value <- List]); -apply_modifier(Modifier, AllowedChars, ExplodeSep, Map) when is_map(Map) -> - {JoinSep, KVSep} = case Modifier of - no_modifier -> {$,, $,}; - explode_modifier -> {ExplodeSep, $=} - end, - lists:reverse(lists:join(JoinSep, - maps:fold(fun(Key, Value, Acc) -> - [[ - urlencode(Key, AllowedChars), - KVSep, - urlencode(Value, AllowedChars) - ]|Acc] - end, [], Map) - )); -apply_modifier({prefix_modifier, MaxLen}, AllowedChars, _, Value) -> - urlencode(string:slice(binarize(Value), 0, MaxLen), AllowedChars); -apply_modifier(_, AllowedChars, _, Value) -> - urlencode(binarize(Value), AllowedChars). - -apply_parameter_modifier(_, _, _, _, _, []) -> - []; -apply_parameter_modifier(_, _, _, _, _, Map) when Map =:= #{} -> - []; -apply_parameter_modifier(no_modifier, AllowedChars, _, _, Name, List) when is_list(List) -> - [ - Name, - $=, - lists:join($,, [urlencode(Value, AllowedChars) || Value <- List]) - ]; -apply_parameter_modifier(explode_modifier, AllowedChars, ExplodeSep, _, Name, List) when is_list(List) -> - lists:join(ExplodeSep, [[ - Name, - $=, - urlencode(Value, AllowedChars) - ] || Value <- List]); -apply_parameter_modifier(Modifier, AllowedChars, ExplodeSep, _, Name, Map) when is_map(Map) -> - {JoinSep, KVSep} = case Modifier of - no_modifier -> {$,, $,}; - explode_modifier -> {ExplodeSep, $=} - end, - [ - case Modifier of - no_modifier -> - [ - Name, - $= - ]; - explode_modifier -> - [] - end, - lists:reverse(lists:join(JoinSep, - maps:fold(fun(Key, Value, Acc) -> - [[ - urlencode(Key, AllowedChars), - KVSep, - urlencode(Value, AllowedChars) - ]|Acc] - end, [], Map) - )) - ]; -apply_parameter_modifier(Modifier, AllowedChars, _, Trim, Name, Value0) -> - Value1 = binarize(Value0), - Value = case Modifier of - {prefix_modifier, MaxLen} -> - string:slice(Value1, 0, MaxLen); - no_modifier -> - Value1 - end, - [ - Name, - case Value of - <<>> when Trim =:= trim -> - []; - <<>> when Trim =:= no_trim -> - $=; - _ -> - [ - $=, - urlencode(Value, AllowedChars) - ] - end - ]. - -binarize(Value) when is_integer(Value) -> - integer_to_binary(Value); -binarize(Value) when is_float(Value) -> - float_to_binary(Value, [{decimals, 10}, compact]); -binarize(Value) -> - Value. - -urlencode(Value, unreserved) -> - urlencode_unreserved(Value, <<>>); -urlencode(Value, reserved) -> - urlencode_reserved(Value, <<>>). - -urlencode_unreserved(<<C,R/bits>>, Acc) - when ?IS_URI_UNRESERVED(C) -> - urlencode_unreserved(R, <<Acc/binary,C>>); -urlencode_unreserved(<<C,R/bits>>, Acc) -> - urlencode_unreserved(R, <<Acc/binary,$%,?HEX(C)>>); -urlencode_unreserved(<<>>, Acc) -> - Acc. - -urlencode_reserved(<<C,R/bits>>, Acc) - when ?IS_URI_UNRESERVED(C) or ?IS_URI_GEN_DELIMS(C) or ?IS_URI_SUB_DELIMS(C) -> - urlencode_reserved(R, <<Acc/binary,C>>); -urlencode_reserved(<<C,R/bits>>, Acc) -> - urlencode_reserved(R, <<Acc/binary,$%,?HEX(C)>>); -urlencode_reserved(<<>>, Acc) -> - Acc. - --ifdef(TEST). -expand_uritemplate_test_() -> - Files = filelib:wildcard("deps/uritemplate-tests/*.json"), - lists:flatten([begin - {ok, JSON} = file:read_file(File), - Tests = jsx:decode(JSON, [return_maps]), - [begin - %% Erlang doesn't have a NULL value. - Vars = maps:remove(<<"undef">>, Vars0), - [ - {iolist_to_binary(io_lib:format("~s - ~s: ~s => ~s", - [filename:basename(File), Section, URITemplate, - if - is_list(Expected) -> lists:join(<<" OR ">>, Expected); - true -> Expected - end - ])), - fun() -> - case Expected of - false -> - {'EXIT', _} = (catch expand(URITemplate, Vars)); - [_|_] -> - Result = iolist_to_binary(expand(URITemplate, Vars)), - io:format("~p", [Result]), - true = lists:member(Result, Expected); - _ -> - Expected = iolist_to_binary(expand(URITemplate, Vars)) - end - end} - || [URITemplate, Expected] <- Cases] - end || {Section, #{ - <<"variables">> := Vars0, - <<"testcases">> := Cases - }} <- maps:to_list(Tests)] - end || File <- Files]). --endif. |