Ada 3.0.1
Fast spec-compliant URL parser
Loading...
Searching...
No Matches
url_pattern.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <optional>
5#include <string>
6
7namespace ada {
8
9tl::expected<url_pattern_init, errors> url_pattern_init::process(
10 url_pattern_init init, std::string_view type,
11 std::optional<std::string_view> protocol,
12 std::optional<std::string_view> username,
13 std::optional<std::string_view> password,
14 std::optional<std::string_view> hostname,
15 std::optional<std::string_view> port,
16 std::optional<std::string_view> pathname,
17 std::optional<std::string_view> search,
18 std::optional<std::string_view> hash) {
19 // Let result be the result of creating a new URLPatternInit.
20 auto result = url_pattern_init{};
21
22 // If protocol is not null, set result["protocol"] to protocol.
23 if (protocol.has_value()) result.protocol = *protocol;
24
25 // If username is not null, set result["username"] to username.
26 if (username.has_value()) result.username = *username;
27
28 // If password is not null, set result["password"] to password.
29 if (password.has_value()) result.password = *password;
30
31 // If hostname is not null, set result["hostname"] to hostname.
32 if (hostname.has_value()) result.hostname = *hostname;
33
34 // If port is not null, set result["port"] to port.
35 if (port.has_value()) result.port = *port;
36
37 // If pathname is not null, set result["pathname"] to pathname.
38 if (pathname.has_value()) result.pathname = *pathname;
39
40 // If search is not null, set result["search"] to search.
41 if (search.has_value()) result.search = *search;
42
43 // If hash is not null, set result["hash"] to hash.
44 if (hash.has_value()) result.hash = *hash;
45
46 // Let baseURL be null.
47 std::optional<url_aggregator> base_url{};
48
49 // If init["baseURL"] exists:
50 if (init.base_url.has_value()) {
51 // Set baseURL to the result of parsing init["baseURL"].
52 auto parsing_result = ada::parse<url_aggregator>(*init.base_url);
53 // If baseURL is failure, then throw a TypeError.
54 if (!parsing_result) {
55 return tl::unexpected(errors::type_error);
56 }
57 base_url = std::move(*parsing_result);
58
59 // If init["protocol"] does not exist, then set result["protocol"] to the
60 // result of processing a base URL string given baseURL’s scheme and type.
61 if (!init.protocol.has_value()) {
62 ADA_ASSERT_TRUE(base_url.has_value());
63 std::string_view base_url_protocol = base_url->get_protocol();
64 if (base_url_protocol.ends_with(":")) base_url_protocol.remove_suffix(1);
65 result.protocol =
66 url_pattern_helpers::process_base_url_string(base_url_protocol, type);
67 }
68
69 // If type is not "pattern" and init contains none of "protocol",
70 // "hostname", "port" and "username", then set result["username"] to the
71 // result of processing a base URL string given baseURL’s username and type.
72 if (type != "pattern" && !init.protocol && !init.hostname && !init.port &&
73 !init.username) {
75 base_url->get_username(), type);
76 }
77
78 // TODO: Optimization opportunity: Merge this with the previous check.
79 // If type is not "pattern" and init contains none of "protocol",
80 // "hostname", "port", "username" and "password", then set
81 // result["password"] to the result of processing a base URL string given
82 // baseURL’s password and type.
83 if (type != "pattern" && !init.protocol && !init.hostname && !init.port &&
84 !init.username && !init.password) {
86 base_url->get_password(), type);
87 }
88
89 // If init contains neither "protocol" nor "hostname", then:
90 if (!init.protocol && !init.hostname) {
91 // Let baseHost be baseURL’s host.
92 // If baseHost is null, then set baseHost to the empty string.
93 auto base_host = base_url->get_hostname();
94 // Set result["hostname"] to the result of processing a base URL string
95 // given baseHost and type.
96 result.hostname =
98 }
99
100 // If init contains none of "protocol", "hostname", and "port", then:
101 if (!init.protocol && !init.hostname && !init.port) {
102 // If baseURL’s port is null, then set result["port"] to the empty string.
103 // Otherwise, set result["port"] to baseURL’s port, serialized.
104 result.port = base_url->get_port();
105 }
106
107 // If init contains none of "protocol", "hostname", "port", and "pathname",
108 // then set result["pathname"] to the result of processing a base URL string
109 // given the result of URL path serializing baseURL and type.
110 if (!init.protocol && !init.hostname && !init.port && !init.pathname) {
112 base_url->get_pathname(), type);
113 }
114
115 // If init contains none of "protocol", "hostname", "port", "pathname", and
116 // "search", then:
117 if (!init.protocol && !init.hostname && !init.port && !init.pathname &&
118 !init.search) {
119 // Let baseQuery be baseURL’s query.
120 // Set result["search"] to the result of processing a base URL string
121 // given baseQuery and type.
123 base_url->get_search(), type);
124 }
125
126 // If init contains none of "protocol", "hostname", "port", "pathname",
127 // "search", and "hash", then:
128 if (!init.protocol && !init.hostname && !init.port && !init.pathname &&
129 !init.search && !init.hash) {
130 // Let baseFragment be baseURL’s fragment.
131 // Set result["hash"] to the result of processing a base URL string given
132 // baseFragment and type.
134 base_url->get_hash(), type);
135 }
136 }
137
138 // If init["protocol"] exists, then set result["protocol"] to the result of
139 // process protocol for init given init["protocol"] and type.
140 if (init.protocol) {
141 auto process_result = process_protocol(*init.protocol, type);
142 if (!process_result) {
143 return tl::unexpected(process_result.error());
144 }
145 result.protocol = std::move(*process_result);
146 }
147
148 // If init["username"] exists, then set result["username"] to the result of
149 // process username for init given init["username"] and type.
150 if (init.username.has_value()) {
151 auto process_result = process_username(*init.username, type);
152 if (!process_result) {
153 return tl::unexpected(process_result.error());
154 }
155 result.username = std::move(*process_result);
156 }
157
158 // If init["password"] exists, then set result["password"] to the result of
159 // process password for init given init["password"] and type.
160 if (init.password.has_value()) {
161 auto process_result = process_password(*init.password, type);
162 if (!process_result) {
163 return tl::unexpected(process_result.error());
164 }
165 result.password = std::move(*process_result);
166 }
167
168 // If init["hostname"] exists, then set result["hostname"] to the result of
169 // process hostname for init given init["hostname"] and type.
170 if (init.hostname.has_value()) {
171 auto process_result = process_hostname(*init.hostname, type);
172 if (!process_result) {
173 return tl::unexpected(process_result.error());
174 }
175 result.hostname = std::move(*process_result);
176 }
177
178 // If init["port"] exists, then set result["port"] to the result of process
179 // port for init given init["port"], result["protocol"], and type.
180 if (init.port) {
181 auto process_result =
182 process_port(*init.port, result.protocol.value_or("fake"), type);
183 if (!process_result) {
184 return tl::unexpected(process_result.error());
185 }
186 result.port = std::move(*process_result);
187 }
188
189 // If init["pathname"] exists:
190 if (init.pathname.has_value()) {
191 // Set result["pathname"] to init["pathname"].
192 result.pathname = init.pathname;
193
194 // If the following are all true:
195 // - baseURL is not null;
196 // - baseURL has an opaque path; and
197 // - the result of running is an absolute pathname given result["pathname"]
198 // and type is false,
199 if (base_url && !base_url->has_opaque_path &&
201 // Let baseURLPath be the result of running process a base URL string
202 // given the result of URL path serializing baseURL and type.
203 std::string base_url_path = url_pattern_helpers::process_base_url_string(
204 base_url->get_pathname(), type);
205
206 // Let slash index be the index of the last U+002F (/) code point found in
207 // baseURLPath, interpreted as a sequence of code points, or null if there
208 // are no instances of the code point.
209 auto slash_index = base_url_path.find_last_of('/');
210
211 // If slash index is not null:
212 if (slash_index != std::string::npos) {
213 // Let new pathname be the code point substring from 0 to slash index +
214 // 1 within baseURLPath.
215 std::string new_pathname = base_url_path.substr(0, slash_index + 1);
216 // Append result["pathname"] to the end of new pathname.
217 ADA_ASSERT_TRUE(result.pathname.has_value());
218 new_pathname.append(result.pathname.value());
219 // Set result["pathname"] to new pathname.
220 result.pathname = std::move(new_pathname);
221 }
222 }
223
224 // Set result["pathname"] to the result of process pathname for init given
225 // result["pathname"], result["protocol"], and type.
226 auto pathname_processing_result =
227 process_pathname(*result.pathname, result.protocol.value_or(""), type);
228 if (!pathname_processing_result) {
229 return tl::unexpected(pathname_processing_result.error());
230 }
231 result.pathname = std::move(*pathname_processing_result);
232 }
233
234 // If init["search"] exists then set result["search"] to the result of process
235 // search for init given init["search"] and type.
236 if (init.search) {
237 auto process_result = process_search(*init.search, type);
238 if (!process_result) {
239 return tl::unexpected(process_result.error());
240 }
241 result.search = std::move(*process_result);
242 }
243
244 // If init["hash"] exists then set result["hash"] to the result of process
245 // hash for init given init["hash"] and type.
246 if (init.hash) {
247 auto process_result = process_hash(*init.hash, type);
248 if (!process_result) {
249 return tl::unexpected(process_result.error());
250 }
251 result.hash = std::move(*process_result);
252 }
253 // Return result.
254 return result;
255}
256
257tl::expected<std::string, errors> url_pattern_init::process_protocol(
258 std::string_view value, std::string_view type) {
259 ada_log("process_protocol=", value, " [", type, "]");
260 // Let strippedValue be the given value with a single trailing U+003A (:)
261 // removed, if any.
262 if (value.ends_with(":")) {
263 value.remove_suffix(1);
264 }
265 // If type is "pattern" then return strippedValue.
266 if (type == "pattern") {
267 return std::string(value);
268 }
269 // Return the result of running canonicalize a protocol given strippedValue.
271}
272
273tl::expected<std::string, errors> url_pattern_init::process_username(
274 std::string_view value, std::string_view type) {
275 // If type is "pattern" then return value.
276 if (type == "pattern") {
277 return std::string(value);
278 }
279 // Return the result of running canonicalize a username given value.
281}
282
283tl::expected<std::string, errors> url_pattern_init::process_password(
284 std::string_view value, std::string_view type) {
285 // If type is "pattern" then return value.
286 if (type == "pattern") {
287 return std::string(value);
288 }
289 // Return the result of running canonicalize a password given value.
291}
292
293tl::expected<std::string, errors> url_pattern_init::process_hostname(
294 std::string_view value, std::string_view type) {
295 ada_log("process_hostname value=", value, " type=", type);
296 // If type is "pattern" then return value.
297 if (type == "pattern") {
298 return std::string(value);
299 }
300 // Return the result of running canonicalize a hostname given value.
302}
303
304tl::expected<std::string, errors> url_pattern_init::process_port(
305 std::string_view port, std::string_view protocol, std::string_view type) {
306 // If type is "pattern" then return portValue.
307 if (type == "pattern") {
308 return std::string(port);
309 }
310 // Return the result of running canonicalize a port given portValue and
311 // protocolValue.
313}
314
315tl::expected<std::string, errors> url_pattern_init::process_pathname(
316 std::string_view value, std::string_view protocol, std::string_view type) {
317 // If type is "pattern" then return pathnameValue.
318 if (type == "pattern") {
319 return std::string(value);
320 }
321
322 // If protocolValue is a special scheme or the empty string, then return the
323 // result of running canonicalize a pathname given pathnameValue.
324 if (protocol.empty() || scheme::is_special(protocol)) {
326 }
327
328 // Return the result of running canonicalize an opaque pathname given
329 // pathnameValue.
331}
332
333tl::expected<std::string, errors> url_pattern_init::process_search(
334 std::string_view value, std::string_view type) {
335 // Let strippedValue be the given value with a single leading U+003F (?)
336 // removed, if any.
337 if (value.starts_with("?")) {
338 value.remove_prefix(1);
339 }
340 ADA_ASSERT_TRUE(!value.starts_with("?"));
341 // If type is "pattern" then return strippedValue.
342 if (type == "pattern") {
343 return std::string(value);
344 }
345 // Return the result of running canonicalize a search given strippedValue.
347}
348
349tl::expected<std::string, errors> url_pattern_init::process_hash(
350 std::string_view value, std::string_view type) {
351 // Let strippedValue be the given value with a single leading U+0023 (#)
352 // removed, if any.
353 if (value.starts_with("#")) {
354 value.remove_prefix(1);
355 }
356 ADA_ASSERT_TRUE(!value.starts_with("#"));
357 // If type is "pattern" then return strippedValue.
358 if (type == "pattern") {
359 return std::string(value);
360 }
361 // Return the result of running canonicalize a hash given strippedValue.
363}
364
365} // namespace ada
#define ADA_ASSERT_TRUE(COND)
tl::expected< std::string, errors > canonicalize_opaque_pathname(std::string_view input)
tl::expected< std::string, errors > canonicalize_pathname(std::string_view input)
constexpr bool is_absolute_pathname(std::string_view input, std::string_view type) noexcept
tl::expected< std::string, errors > canonicalize_password(std::string_view input)
std::string process_base_url_string(std::string_view input, std::string_view type)
tl::expected< std::string, errors > canonicalize_protocol(std::string_view input)
tl::expected< std::string, errors > canonicalize_hostname(std::string_view input)
tl::expected< std::string, errors > canonicalize_port_with_protocol(std::string_view input, std::string_view protocol)
tl::expected< std::string, errors > canonicalize_hash(std::string_view input)
tl::expected< std::string, errors > canonicalize_search(std::string_view input)
tl::expected< std::string, errors > canonicalize_username(std::string_view input)
Definition ada_idna.h:13
@ type_error
Definition errors.h:10
template ada::result< url_aggregator > parse< url_aggregator >(std::string_view input, const url_aggregator *base_url)
tl::expected< result_type, ada::errors > result
std::optional< std::string > port
static tl::expected< std::string, errors > process_pathname(std::string_view value, std::string_view protocol, std::string_view type)
static tl::expected< std::string, errors > process_username(std::string_view value, std::string_view type)
static tl::expected< std::string, errors > process_port(std::string_view port, std::string_view protocol, std::string_view type)
static tl::expected< std::string, errors > process_hostname(std::string_view value, std::string_view type)
std::optional< std::string > protocol
static tl::expected< url_pattern_init, errors > process(url_pattern_init init, std::string_view type, std::optional< std::string_view > protocol=std::nullopt, std::optional< std::string_view > username=std::nullopt, std::optional< std::string_view > password=std::nullopt, std::optional< std::string_view > hostname=std::nullopt, std::optional< std::string_view > port=std::nullopt, std::optional< std::string_view > pathname=std::nullopt, std::optional< std::string_view > search=std::nullopt, std::optional< std::string_view > hash=std::nullopt)
std::optional< std::string > password
std::optional< std::string > base_url
std::optional< std::string > hostname
std::optional< std::string > search
static tl::expected< std::string, errors > process_hash(std::string_view value, std::string_view type)
static tl::expected< std::string, errors > process_password(std::string_view value, std::string_view type)
std::optional< std::string > username
std::optional< std::string > pathname
static tl::expected< std::string, errors > process_search(std::string_view value, std::string_view type)
std::optional< std::string > hash
static tl::expected< std::string, errors > process_protocol(std::string_view value, std::string_view type)
Declaration for the URLPattern inline functions.