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