@@ 0,0 1,242 @@
+module Var = struct
+ type v =
+ | S of string
+ | A of string list
+ | M of (string * string) list
+
+ type t = string * v
+end
+
+module Expandspec = struct
+ type t = {
+ kv : bool;
+ sep : string;
+ lead_char : char option;
+ encode : string -> string;
+ }
+end
+
+type varspec = {
+ name : string;
+ prefix : int option;
+ explode : bool;
+}
+
+type expr =
+ | Literal of string
+ | Expr of {
+ op : char option;
+ vars : varspec list;
+ }
+
+type t = expr list
+
+type parse_err = [ `Error ]
+
+let rec exprs_of_tokens =
+ let module T = Uritmpl_lexer.Token in
+ function
+ | [] -> []
+ | T.Literal s :: ts -> Literal s :: exprs_of_tokens ts
+ | T.Open_expr :: ts -> expr_of_tokens ts
+ | _ -> assert false
+
+and expr_of_tokens =
+ let module T = Uritmpl_lexer.Token in
+ function
+ | T.Op op :: ts ->
+ let (vars, ts) = vars_of_tokens [] ts in
+ Expr { op = Some op; vars } :: exprs_of_tokens ts
+ | ts ->
+ let (vars, ts) = vars_of_tokens [] ts in
+ Expr { op = None; vars } :: exprs_of_tokens ts
+
+and vars_of_tokens acc =
+ let module T = Uritmpl_lexer.Token in
+ function
+ | T.Close_expr :: ts -> (List.rev acc, ts)
+ | T.Var_sep :: ts -> vars_of_tokens acc ts
+ | T.Var name :: T.Prefix prefix :: T.Explode :: ts ->
+ vars_of_tokens ({ name; prefix = Some prefix; explode = true } :: acc) ts
+ | T.Var name :: T.Prefix prefix :: ts ->
+ vars_of_tokens ({ name; prefix = Some prefix; explode = false } :: acc) ts
+ | T.Var name :: T.Explode :: ts ->
+ vars_of_tokens ({ name; prefix = None; explode = true } :: acc) ts
+ | T.Var name :: ts -> vars_of_tokens ({ name; prefix = None; explode = false } :: acc) ts
+ | _ -> assert false
+
+let of_string s =
+ match Uritmpl_lexer.tokenize s with
+ | Ok tokens -> Ok (exprs_of_tokens tokens)
+ | Error err -> Error `Error
+
+let varspec_to_string buf vars =
+ let strs =
+ List.map
+ (function
+ | { name; prefix = Some n; explode = true } -> Printf.sprintf "%s:%d*" name n
+ | { name; prefix = Some n; _ } -> Printf.sprintf "%s:%d" name n
+ | { name; prefix = None; explode = true } -> name ^ "*"
+ | { name; _ } -> name)
+ vars
+ in
+ Buffer.add_string buf (String.concat "," strs)
+
+let to_string t =
+ let buf = Buffer.create 20 in
+ List.iter
+ (function
+ | Literal s -> Buffer.add_string buf s
+ | Expr { op = Some op; vars } ->
+ Buffer.add_char buf '{';
+ Buffer.add_char buf op;
+ varspec_to_string buf vars;
+ Buffer.add_char buf '}'
+ | Expr { vars; _ } ->
+ Buffer.add_char buf '{';
+ varspec_to_string buf vars;
+ Buffer.add_char buf '}')
+ t;
+ Buffer.contents buf
+
+let expand_value spec v value =
+ let open Expandspec in
+ match value with
+ | Var.S s -> (
+ match (spec, v) with
+ | ({ kv = true; encode; _ }, { name; prefix = Some prefix; _ }) ->
+ Printf.sprintf "%s=%s" name (encode (String.sub s 0 (min prefix (String.length s))))
+ | ({ kv = false; encode; _ }, { name; prefix = Some prefix; _ }) ->
+ encode (String.sub s 0 (min prefix (String.length s)))
+ | ({ kv = true; encode; lead_char; _ }, { name; _ }) -> (
+ match lead_char with
+ | Some ';' when s = "" -> name
+ | _ -> Printf.sprintf "%s=%s" name (encode s) )
+ | ({ kv = false; encode; _ }, _) -> encode s )
+ | Var.A l -> (
+ match (spec, v) with
+ | ({ kv = true; encode; _ }, { name; explode = false; _ }) ->
+ Printf.sprintf "%s=%s" name (String.concat "," (List.map encode l))
+ | ({ kv = true; encode; sep; lead_char; _ }, { name; explode = true; _ }) ->
+ String.concat sep (List.map (fun v -> Printf.sprintf "%s=%s" name (encode v)) l)
+ | ({ kv = false; encode; _ }, { name; explode = false; _ }) ->
+ String.concat "," (List.map encode l)
+ | ({ kv = false; encode; sep; _ }, { name; explode = true; _ }) ->
+ String.concat sep (List.map encode l) )
+ | Var.M m -> (
+ match (spec, v) with
+ | ({ kv = true; encode; _ }, { name; explode = false; _ }) ->
+ Printf.sprintf
+ "%s=%s"
+ name
+ (String.concat "," (List.map (fun (k, v) -> encode k ^ "," ^ encode v) m))
+ | ({ kv = true; encode; sep; _ }, { name; explode = true; _ }) ->
+ String.concat sep (List.map (fun (k, v) -> encode k ^ "=" ^ encode v) m)
+ | ({ kv = false; encode; _ }, { name; explode = false; _ }) ->
+ String.concat "," (List.map (fun (k, v) -> encode k ^ "," ^ encode v) m)
+ | ({ kv = false; encode; sep; _ }, { name; explode = true; _ }) ->
+ String.concat sep (List.map (fun (k, v) -> encode k ^ "=" ^ encode v) m) )
+
+let expand_var spec vars v =
+ match List.assoc_opt v.name vars with
+ | Some (Var.M []) -> None
+ | Some value -> Some (expand_value spec v value)
+ | None -> None
+
+let expand t vars =
+ let buf = Buffer.create 20 in
+ List.iter
+ (function
+ | Literal s -> Buffer.add_string buf s
+ | Expr { op; vars = vs } -> (
+ let expand_spec =
+ match op with
+ | None ->
+ Expandspec.
+ {
+ kv = false;
+ sep = ",";
+ lead_char = None;
+ encode = (fun s -> Uri.pct_encode ~component:`Authority s);
+ }
+ | Some '+' ->
+ Expandspec.
+ {
+ kv = false;
+ sep = ",";
+ lead_char = None;
+ encode =
+ (fun s ->
+ Uri.pct_encode ~component:(`Custom (`Path, ":/?#[]@!$&'()*+,;=", "")) s);
+ }
+ | Some '#' ->
+ Expandspec.
+ {
+ kv = false;
+ sep = ",";
+ lead_char = Some '#';
+ encode = (fun s -> Uri.pct_encode s);
+ }
+ | Some '.' ->
+ Expandspec.
+ {
+ kv = false;
+ sep = ".";
+ lead_char = Some '.';
+ encode = (fun s -> Uri.pct_encode ~component:`Authority s);
+ }
+ | Some '/' ->
+ Expandspec.
+ {
+ kv = false;
+ sep = "/";
+ lead_char = Some '/';
+ encode = (fun s -> Uri.pct_encode ~component:`Authority s);
+ }
+ | Some ';' ->
+ Expandspec.
+ {
+ kv = true;
+ sep = ";";
+ lead_char = Some ';';
+ encode = (fun s -> Uri.pct_encode ~component:`Authority s);
+ }
+ | Some '?' ->
+ Expandspec.
+ {
+ kv = true;
+ sep = "&";
+ lead_char = Some '?';
+ encode = (fun s -> Uri.pct_encode ~component:`Query_value s);
+ }
+ | Some '&' ->
+ Expandspec.
+ {
+ kv = true;
+ sep = "&";
+ lead_char = Some '&';
+ encode = (fun s -> Uri.pct_encode ~component:`Query_value s);
+ }
+ | _ -> assert false
+ in
+ let res =
+ vs
+ |> List.map (fun v ->
+ match expand_var expand_spec vars v with
+ | Some r -> [ r ]
+ | None -> [])
+ |> List.flatten
+ in
+ match res with
+ | [] -> ()
+ | r ->
+ let s = String.concat expand_spec.Expandspec.sep r in
+ let lead =
+ Option.value
+ (Option.map (String.make 1) expand_spec.Expandspec.lead_char)
+ ~default:""
+ in
+ Buffer.add_string buf lead;
+ Buffer.add_string buf s ))
+ t;
+ Buffer.contents buf
@@ 0,0 1,314 @@
+let test_vars =
+ let open Uritmpl.Var in
+ [
+ ("count", A [ "one"; "two"; "three" ]);
+ ("dom", A [ "example"; "com" ]);
+ ("dub", S "me/too");
+ ("hello", S "Hello World!");
+ ("half", S "50%");
+ ("var", S "value");
+ ("who", S "fred");
+ ("base", S "http://example.com/home/");
+ ("path", S "/foo/bar");
+ ("list", A [ "red"; "green"; "blue" ]);
+ ("keys", M [ ("semi", ";"); ("dot", "."); ("comma", ",") ]);
+ ("v", S "6");
+ ("x", S "1024");
+ ("y", S "768");
+ ("empty", S "");
+ ("empty_keys", M []);
+ ]
+
+let expand s =
+ match Uritmpl.of_string s with
+ | Ok tmpl -> Uritmpl.expand tmpl test_vars
+ | Error _ -> assert false
+
+let of_string_to_string_matches s =
+ match Uritmpl.of_string s with
+ | Ok tmpl -> s = Uritmpl.to_string tmpl
+ | Error _ -> failwith s
+
+let test_no_variable_expansion =
+ Oth.test ~name:"No Variables" (fun _ -> assert (expand "foo" = "foo"))
+
+let test_variable_expansion_3_2_1 =
+ Oth.test ~name:"Variable Expansion 3.2.1" (fun _ ->
+ assert (expand "{count}" = "one,two,three");
+ assert (expand "{count*}" = "one,two,three");
+ assert (expand "{/count}" = "/one,two,three");
+ assert (expand "{/count*}" = "/one/two/three");
+ assert (expand "{;count}" = ";count=one,two,three");
+ assert (expand "{;count*}" = ";count=one;count=two;count=three");
+ assert (expand "{?count}" = "?count=one,two,three");
+ assert (expand "{?count*}" = "?count=one&count=two&count=three");
+ assert (expand "{&count*}" = "&count=one&count=two&count=three"))
+
+let test_simple_string_expansion_3_2_2 =
+ Oth.test ~name:"Simple String Expansion 3.2.2" (fun _ ->
+ assert (expand "{var}" = "value");
+ assert (expand "{hello}" = "Hello%20World%21");
+ assert (expand "{half}" = "50%25");
+ assert (expand "O{empty}X" = "OX");
+ assert (expand "O{undef}X" = "OX");
+ assert (expand "{x,y}" = "1024,768");
+ assert (expand "{x,hello,y}" = "1024,Hello%20World%21,768");
+ assert (expand "?{x,empty}" = "?1024,");
+ assert (expand "?{x,undef}" = "?1024");
+ assert (expand "?{undef,y}" = "?768");
+ assert (expand "{var:3}" = "val");
+ assert (expand "{var:30}" = "value");
+ assert (expand "{list}" = "red,green,blue");
+ assert (expand "{list*}" = "red,green,blue");
+ assert (expand "{keys}" = "semi,%3B,dot,.,comma,%2C");
+ assert (expand "{keys*}" = "semi=%3B,dot=.,comma=%2C"))
+
+let test_reserved_expansion_3_2_3 =
+ Oth.test ~name:"Reserved Expansion 3.2.3" (fun _ ->
+ assert (expand "{+var}" = "value");
+ assert (expand "{+hello}" = "Hello%20World!");
+ assert (expand "{+half}" = "50%25");
+ assert (expand "{base}index" = "http%3A%2F%2Fexample.com%2Fhome%2Findex");
+ assert (expand "{+base}index" = "http://example.com/home/index");
+ assert (expand "O{+empty}X" = "OX");
+ assert (expand "O{+undef}X" = "OX");
+ assert (expand "{+path}/here" = "/foo/bar/here");
+ assert (expand "here?ref={+path}" = "here?ref=/foo/bar");
+ assert (expand "up{+path}{var}/here" = "up/foo/barvalue/here");
+ assert (expand "{+x,hello,y}" = "1024,Hello%20World!,768");
+ assert (expand "{+path,x}/here" = "/foo/bar,1024/here");
+ assert (expand "{+path:6}/here" = "/foo/b/here");
+ assert (expand "{+list}" = "red,green,blue");
+ assert (expand "{+list*}" = "red,green,blue");
+ assert (expand "{+keys}" = "semi,;,dot,.,comma,,");
+ assert (expand "{+keys*}" = "semi=;,dot=.,comma=,"))
+
+let test_fragment_expansion_3_2_4 =
+ Oth.test ~name:"Fragment Expansion 3.2.4" (fun _ ->
+ assert (expand "{#var}" = "#value");
+ assert (expand "{#hello}" = "#Hello%20World!");
+ assert (expand "{#half}" = "#50%25");
+ assert (expand "foo{#empty}" = "foo#");
+ assert (expand "foo{#undef}" = "foo");
+ assert (expand "{#x,hello,y}" = "#1024,Hello%20World!,768");
+ assert (expand "{#path,x}/here" = "#/foo/bar,1024/here");
+ assert (expand "{#path:6}/here" = "#/foo/b/here");
+ assert (expand "{#list}" = "#red,green,blue");
+ assert (expand "{#list*}" = "#red,green,blue");
+ assert (expand "{#keys}" = "#semi,;,dot,.,comma,,");
+ assert (expand "{#keys*}" = "#semi=;,dot=.,comma=,"))
+
+let test_label_expansion_with_dot_prefix_3_2_5 =
+ Oth.test ~name:"Label Expansion With Dot Prefix 3.2.5" (fun _ ->
+ assert (expand "{.who}" = ".fred");
+ assert (expand "{.who,who}" = ".fred.fred");
+ assert (expand "{.half,who}" = ".50%25.fred");
+ assert (expand "www{.dom*}" = "www.example.com");
+ assert (expand "X{.var}" = "X.value");
+ assert (expand "X{.empty}" = "X.");
+ assert (expand "X{.undef}" = "X");
+ assert (expand "X{.var:3}" = "X.val");
+ assert (expand "X{.list}" = "X.red,green,blue");
+ assert (expand "X{.list*}" = "X.red.green.blue");
+ assert (expand "X{.keys}" = "X.semi,%3B,dot,.,comma,%2C");
+ assert (expand "X{.keys*}" = "X.semi=%3B.dot=..comma=%2C");
+ assert (expand "X{.empty_keys}" = "X");
+ assert (expand "X{.empty_keys*}" = "X"))
+
+let test_path_segment_expansion_3_2_6 =
+ Oth.test ~name:"Path Segment Expansion 3.2.6" (fun _ ->
+ assert (expand "{/who}" = "/fred");
+ assert (expand "{/who,who}" = "/fred/fred");
+ assert (expand "{/half,who}" = "/50%25/fred");
+ assert (expand "{/who,dub}" = "/fred/me%2Ftoo");
+ assert (expand "{/var}" = "/value");
+ assert (expand "{/var,empty}" = "/value/");
+ assert (expand "{/var,undef}" = "/value");
+ assert (expand "{/var,x}/here" = "/value/1024/here");
+ assert (expand "{/var:1,var}" = "/v/value");
+ assert (expand "{/list}" = "/red,green,blue");
+ assert (expand "{/list*}" = "/red/green/blue");
+ assert (expand "{/list*,path:4}" = "/red/green/blue/%2Ffoo");
+ assert (expand "{/keys}" = "/semi,%3B,dot,.,comma,%2C");
+ assert (expand "{/keys*}" = "/semi=%3B/dot=./comma=%2C"))
+
+let test_path_style_parameter_expansion_3_2_7 =
+ Oth.test ~name:"Path-Style Parameter Expansion 3.2.7" (fun _ ->
+ assert (expand "{;who}" = ";who=fred");
+ assert (expand "{;half}" = ";half=50%25");
+ assert (expand "{;empty}" = ";empty");
+ assert (expand "{;v,empty,who}" = ";v=6;empty;who=fred");
+ assert (expand "{;v,bar,who}" = ";v=6;who=fred");
+ assert (expand "{;x,y}" = ";x=1024;y=768");
+ assert (expand "{;x,y,empty}" = ";x=1024;y=768;empty");
+ assert (expand "{;x,y,undef}" = ";x=1024;y=768");
+ assert (expand "{;hello:5}" = ";hello=Hello");
+ assert (expand "{;list}" = ";list=red,green,blue");
+ assert (expand "{;list*}" = ";list=red;list=green;list=blue");
+ assert (expand "{;keys}" = ";keys=semi,%3B,dot,.,comma,%2C");
+ assert (expand "{;keys*}" = ";semi=%3B;dot=.;comma=%2C"))
+
+let test_form_style_query_expansion_3_2_8 =
+ Oth.test ~name:"Form-Style Query Expansion 3.2.8" (fun _ ->
+ assert (expand "{?who}" = "?who=fred");
+ assert (expand "{?half}" = "?half=50%25");
+ assert (expand "{?x,y}" = "?x=1024&y=768");
+ assert (expand "{?x,y,empty}" = "?x=1024&y=768&empty=");
+ assert (expand "{?x,y,undef}" = "?x=1024&y=768");
+ assert (expand "{?var:3}" = "?var=val");
+ assert (expand "{?list}" = "?list=red,green,blue");
+ assert (expand "{?list*}" = "?list=red&list=green&list=blue");
+ assert (expand "{?keys}" = "?keys=semi,%3B,dot,.,comma,%2C");
+ assert (expand "{?keys*}" = "?semi=%3B&dot=.&comma=%2C"))
+
+let test_form_style_query_continuation_3_2_9 =
+ Oth.test ~name:"Form-Style Query Continuation 3.2.9" (fun _ ->
+ assert (expand "{&who}" = "&who=fred");
+ assert (expand "{&half}" = "&half=50%25");
+ assert (expand "?fixed=yes{&x}" = "?fixed=yes&x=1024");
+ assert (expand "{&x,y,empty}" = "&x=1024&y=768&empty=");
+ assert (expand "{&x,y,undef}" = "&x=1024&y=768");
+ assert (expand "{&var:3}" = "&var=val");
+ assert (expand "{&list}" = "&list=red,green,blue");
+ assert (expand "{&list*}" = "&list=red&list=green&list=blue");
+ assert (expand "{&keys}" = "&keys=semi,%3B,dot,.,comma,%2C");
+ assert (expand "{&keys*}" = "&semi=%3B&dot=.&comma=%2C"))
+
+let test_of_string_to_string =
+ Oth.test ~name:"of_string to_string matches" (fun _ ->
+ assert (of_string_to_string_matches "foo");
+ assert (of_string_to_string_matches "{count}");
+ assert (of_string_to_string_matches "{count*}");
+ assert (of_string_to_string_matches "{/count}");
+ assert (of_string_to_string_matches "{/count*}");
+ assert (of_string_to_string_matches "{;count}");
+ assert (of_string_to_string_matches "{;count*}");
+ assert (of_string_to_string_matches "{?count}");
+ assert (of_string_to_string_matches "{?count*}");
+ assert (of_string_to_string_matches "{&count*}");
+ assert (of_string_to_string_matches "{var}");
+ assert (of_string_to_string_matches "{hello}");
+ assert (of_string_to_string_matches "{half}");
+ assert (of_string_to_string_matches "O{empty}X");
+ assert (of_string_to_string_matches "O{undef}X");
+ assert (of_string_to_string_matches "{x,y}");
+ assert (of_string_to_string_matches "{x,hello,y}");
+ assert (of_string_to_string_matches "?{x,empty}");
+ assert (of_string_to_string_matches "?{x,undef}");
+ assert (of_string_to_string_matches "?{undef,y}");
+ assert (of_string_to_string_matches "{var:3}");
+ assert (of_string_to_string_matches "{var:30}");
+ assert (of_string_to_string_matches "{list}");
+ assert (of_string_to_string_matches "{list*}");
+ assert (of_string_to_string_matches "{keys}");
+ assert (of_string_to_string_matches "{keys*}");
+ assert (of_string_to_string_matches "{+var}");
+ assert (of_string_to_string_matches "{+hello}");
+ assert (of_string_to_string_matches "{+half}");
+ assert (of_string_to_string_matches "{base}index");
+ assert (of_string_to_string_matches "{+base}index");
+ assert (of_string_to_string_matches "O{+empty}X");
+ assert (of_string_to_string_matches "O{+undef}X");
+ assert (of_string_to_string_matches "{+path}/here");
+ assert (of_string_to_string_matches "here?ref={+path}");
+ assert (of_string_to_string_matches "up{+path}{var}/here");
+ assert (of_string_to_string_matches "{+x,hello,y}");
+ assert (of_string_to_string_matches "{+path,x}/here");
+ assert (of_string_to_string_matches "{+path:6}/here");
+ assert (of_string_to_string_matches "{+list}");
+ assert (of_string_to_string_matches "{+list*}");
+ assert (of_string_to_string_matches "{+keys}");
+ assert (of_string_to_string_matches "{+keys*}");
+ assert (of_string_to_string_matches "{#var}");
+ assert (of_string_to_string_matches "{#hello}");
+ assert (of_string_to_string_matches "{#half}");
+ assert (of_string_to_string_matches "foo{#empty}");
+ assert (of_string_to_string_matches "foo{#undef}");
+ assert (of_string_to_string_matches "{#x,hello,y}");
+ assert (of_string_to_string_matches "{#path,x}/here");
+ assert (of_string_to_string_matches "{#path:6}/here");
+ assert (of_string_to_string_matches "{#list}");
+ assert (of_string_to_string_matches "{#list*}");
+ assert (of_string_to_string_matches "{#keys}");
+ assert (of_string_to_string_matches "{#keys*}");
+ assert (of_string_to_string_matches "{.who}");
+ assert (of_string_to_string_matches "{.who,who}");
+ assert (of_string_to_string_matches "{.half,who}");
+ assert (of_string_to_string_matches "www{.dom*}");
+ assert (of_string_to_string_matches "X{.var}");
+ assert (of_string_to_string_matches "X{.empty}");
+ assert (of_string_to_string_matches "X{.undef}");
+ assert (of_string_to_string_matches "X{.var:3}");
+ assert (of_string_to_string_matches "X{.list}");
+ assert (of_string_to_string_matches "X{.list*}");
+ assert (of_string_to_string_matches "X{.keys}");
+ assert (of_string_to_string_matches "X{.keys*}");
+ assert (of_string_to_string_matches "X{.empty_keys}");
+ assert (of_string_to_string_matches "X{.empty_keys*}");
+ assert (of_string_to_string_matches "{/who}");
+ assert (of_string_to_string_matches "{/who,who}");
+ assert (of_string_to_string_matches "{/half,who}");
+ assert (of_string_to_string_matches "{/who,dub}");
+ assert (of_string_to_string_matches "{/var}");
+ assert (of_string_to_string_matches "{/var,empty}");
+ assert (of_string_to_string_matches "{/var,undef}");
+ assert (of_string_to_string_matches "{/var,x}/here");
+ assert (of_string_to_string_matches "{/var:1,var}");
+ assert (of_string_to_string_matches "{/list}");
+ assert (of_string_to_string_matches "{/list*}");
+ assert (of_string_to_string_matches "{/list*,path:4}");
+ assert (of_string_to_string_matches "{/keys}");
+ assert (of_string_to_string_matches "{/keys*}");
+ assert (of_string_to_string_matches "{;who}");
+ assert (of_string_to_string_matches "{;half}");
+ assert (of_string_to_string_matches "{;empty}");
+ assert (of_string_to_string_matches "{;v,empty,who}");
+ assert (of_string_to_string_matches "{;v,bar,who}");
+ assert (of_string_to_string_matches "{;x,y}");
+ assert (of_string_to_string_matches "{;x,y,empty}");
+ assert (of_string_to_string_matches "{;x,y,undef}");
+ assert (of_string_to_string_matches "{;hello:5}");
+ assert (of_string_to_string_matches "{;list}");
+ assert (of_string_to_string_matches "{;list*}");
+ assert (of_string_to_string_matches "{;keys}");
+ assert (of_string_to_string_matches "{;keys*}");
+ assert (of_string_to_string_matches "{?who}");
+ assert (of_string_to_string_matches "{?half}");
+ assert (of_string_to_string_matches "{?x,y}");
+ assert (of_string_to_string_matches "{?x,y,empty}");
+ assert (of_string_to_string_matches "{?x,y,undef}");
+ assert (of_string_to_string_matches "{?var:3}");
+ assert (of_string_to_string_matches "{?list}");
+ assert (of_string_to_string_matches "{?list*}");
+ assert (of_string_to_string_matches "{?keys}");
+ assert (of_string_to_string_matches "{?keys*}");
+ assert (of_string_to_string_matches "{&who}");
+ assert (of_string_to_string_matches "{&half}");
+ assert (of_string_to_string_matches "?fixed=yes{&x}");
+ assert (of_string_to_string_matches "{&x,y,empty}");
+ assert (of_string_to_string_matches "{&x,y,undef}");
+ assert (of_string_to_string_matches "{&var:3}");
+ assert (of_string_to_string_matches "{&list}");
+ assert (of_string_to_string_matches "{&list*}");
+ assert (of_string_to_string_matches "{&keys}");
+ assert (of_string_to_string_matches "{&keys*}"))
+
+let test =
+ Oth.parallel
+ [
+ test_no_variable_expansion;
+ test_variable_expansion_3_2_1;
+ test_simple_string_expansion_3_2_2;
+ test_reserved_expansion_3_2_3;
+ test_fragment_expansion_3_2_4;
+ test_label_expansion_with_dot_prefix_3_2_5;
+ test_path_segment_expansion_3_2_6;
+ test_path_style_parameter_expansion_3_2_7;
+ test_form_style_query_expansion_3_2_8;
+ test_form_style_query_continuation_3_2_9;
+ test_of_string_to_string;
+ ]
+
+let () =
+ Random.self_init ();
+ Oth.run test