Source file uritemplate.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
module String = Stdcompat.String
module List = Stdcompat.List
type expansion_type =
| Simple
| Reserved
| Fragment
| Dot
| PathSegment
| PathParameter
| FormQuery
| FormQueryContinuation
let expansion_type_of_string = function
| "+" -> Reserved
| "#" -> Fragment
| "." -> Dot
| "/" -> PathSegment
| ";" -> PathParameter
| "?" -> FormQuery
| "&" -> FormQueryContinuation
| _ -> Simple
let string_of_expansion_type = function
| Reserved -> "+"
| Fragment -> "#"
| Dot -> "."
| PathSegment -> "/"
| PathParameter -> ";"
| FormQuery -> "?"
| FormQueryContinuation -> "&"
| Simple -> ""
let separator_for_expansion_type = function
| Simple | Reserved | Fragment -> ','
| Dot -> '.'
| PathSegment -> '/'
| PathParameter -> ';'
| FormQuery | FormQueryContinuation -> '&'
let re_for_tokens = Str.regexp "{[^{]+}\\|[^{}]+"
let re_for_is_var_expr = Str.regexp "^{.*}"
let re_for_prefix = Str.regexp "{\\([\\.#+/\\.;\\?&]?\\)\\([a-zA-Z0-9\\.%,_\\*:]+\\)}"
let is_var_expr v = Str.string_match re_for_is_var_expr v 0
let encode_char c =
Char.code c
|> Printf.sprintf "%%%X"
let encode_str rex =
Str.global_substitute
rex
(fun m -> String.get (Str.matched_string m) 0
|> encode_char)
let re_for_encode_reserved = Str.regexp "[^A-Za-z0-9-_.~*'()]"
let uri_encode_reserved = encode_str re_for_encode_reserved
let re_for_encode_full = Str.regexp "[^A-Za-z0-9;,/\\?:@&=\\+$-_\\.!~\\*'()#]"
let uri_encode_full = encode_str re_for_encode_full
let get_var expr_type var_name variables =
match List.assoc_opt var_name variables, expr_type with
| None, _ -> None
| Some var, Fragment
| Some var, Reserved -> Some (uri_encode_full var)
| Some var, _ -> Some (uri_encode_reserved var)
let simple_expr buff _ var =
Buffer.add_string buff var
let form_query buff var_name var =
Buffer.add_string buff var_name; Buffer.add_char buff '='; Buffer.add_string buff var
let path_parameter buff var_name var =
Buffer.add_string buff var_name;
match var with
| "" -> ()
| var -> Buffer.add_char buff '='; Buffer.add_string buff var
let determine_expr_function buff expr_type =
buff
|> match expr_type with
| FormQuery | FormQueryContinuation -> form_query
| PathParameter -> path_parameter
| _ -> simple_expr
let add_var_to_buff buff variables expr_type =
let sep_str = separator_for_expansion_type expr_type in
let f = determine_expr_function buff expr_type in
fun var_name -> match get_var expr_type var_name variables with
| None -> ()
| Some var -> f var_name var; Buffer.add_char buff sep_str
let create_buffer expr_type =
let buff = Buffer.create 10 in
match expr_type with
| Reserved -> buff
| _ -> string_of_expansion_type expr_type |> Buffer.add_string buff; buff
let replace_variable ~variables str =
match is_var_expr str with
| false -> uri_encode_full str
| true ->
let _ = Str.string_match re_for_prefix str 0 in
let expansion_type = Str.matched_group 1 str |> expansion_type_of_string in
let vars = Str.matched_group 2 str |> String.split_on_char ',' in
let buff = create_buffer expansion_type in
List.iter (add_var_to_buff buff variables expansion_type) vars;
match Buffer.length buff - 1 with
| -1 | 0 -> ""
| len -> Buffer.sub buff 0 len
let template_uri ~template ~variables =
let buff = Buffer.create 10 in
let rec aux index =
if Str.string_match re_for_tokens template index && index < (String.length template) then
let new_index = Str.match_end () in
let replaced_var = Str.matched_string template |> replace_variable ~variables in
Buffer.add_string buff replaced_var;
aux new_index
else
Buffer.contents buff
in
aux 0