Ada 3.4.0
Fast spec-compliant URL parser
Loading...
Searching...
No Matches
parser-inl.h
Go to the documentation of this file.
1
4#ifndef ADA_PARSER_INL_H
5#define ADA_PARSER_INL_H
6
7#include "ada/expected.h"
8#include "ada/url_pattern.h"
10#include "ada/parser.h"
11
12#include <string>
13#include <string_view>
14#include <variant>
15
16namespace ada::parser {
17#if ADA_INCLUDE_URL_PATTERN
18template <url_pattern_regex::regex_concept regex_provider>
19tl::expected<url_pattern<regex_provider>, errors> parse_url_pattern_impl(
20 std::variant<std::string_view, url_pattern_init>&& input,
21 const std::string_view* base_url, const url_pattern_options* options) {
22 // Let init be null.
23 url_pattern_init init;
24
25 // If input is a scalar value string then:
26 if (std::holds_alternative<std::string_view>(input)) {
27 // Set init to the result of running parse a constructor string given input.
28 auto parse_result =
29 url_pattern_helpers::constructor_string_parser<regex_provider>::parse(
30 std::get<std::string_view>(input));
31 if (!parse_result) {
32 ada_log("constructor_string_parser::parse failed");
33 return tl::unexpected(parse_result.error());
34 }
35 init = std::move(*parse_result);
36 // If baseURL is null and init["protocol"] does not exist, then throw a
37 // TypeError.
38 if (!base_url && !init.protocol) {
39 ada_log("base url is null and protocol is not set");
40 return tl::unexpected(errors::type_error);
41 }
42
43 // If baseURL is not null, set init["baseURL"] to baseURL.
44 if (base_url) {
45 init.base_url = std::string(*base_url);
46 }
47 } else {
48 // Assert: input is a URLPatternInit.
49 ADA_ASSERT_TRUE(std::holds_alternative<url_pattern_init>(input));
50 // If baseURL is not null, then throw a TypeError.
51 if (base_url) {
52 ada_log("base url is not null");
53 return tl::unexpected(errors::type_error);
54 }
55 // Optimization: Avoid copy by moving the input value.
56 // Set init to input.
57 init = std::move(std::get<url_pattern_init>(input));
58 }
59
60 // Let processedInit be the result of process a URLPatternInit given init,
61 // "pattern", null, null, null, null, null, null, null, and null.
62 auto processed_init =
63 url_pattern_init::process(init, url_pattern_init::process_type::pattern);
64 if (!processed_init) {
65 ada_log("url_pattern_init::process failed for init and 'pattern'");
66 return tl::unexpected(processed_init.error());
67 }
68
69 // For each componentName of "protocol", "username", "password", "hostname",
70 // "port", "pathname", "search", "hash" If processedInit[componentName] does
71 // not exist, then set processedInit[componentName] to "*".
72 ADA_ASSERT_TRUE(processed_init.has_value());
73 if (!processed_init->protocol) processed_init->protocol = "*";
74 if (!processed_init->username) processed_init->username = "*";
75 if (!processed_init->password) processed_init->password = "*";
76 if (!processed_init->hostname) processed_init->hostname = "*";
77 if (!processed_init->port) processed_init->port = "*";
78 if (!processed_init->pathname) processed_init->pathname = "*";
79 if (!processed_init->search) processed_init->search = "*";
80 if (!processed_init->hash) processed_init->hash = "*";
81
82 ada_log("-- processed_init->protocol: ", processed_init->protocol.value());
83 ada_log("-- processed_init->username: ", processed_init->username.value());
84 ada_log("-- processed_init->password: ", processed_init->password.value());
85 ada_log("-- processed_init->hostname: ", processed_init->hostname.value());
86 ada_log("-- processed_init->port: ", processed_init->port.value());
87 ada_log("-- processed_init->pathname: ", processed_init->pathname.value());
88 ada_log("-- processed_init->search: ", processed_init->search.value());
89 ada_log("-- processed_init->hash: ", processed_init->hash.value());
90
91 // If processedInit["protocol"] is a special scheme and processedInit["port"]
92 // is a string which represents its corresponding default port in radix-10
93 // using ASCII digits then set processedInit["port"] to the empty string.
94 // TODO: Optimization opportunity.
95 if (scheme::is_special(*processed_init->protocol)) {
96 std::string_view port = processed_init->port.value();
97 if (std::to_string(scheme::get_special_port(*processed_init->protocol)) ==
98 port) {
99 processed_init->port->clear();
100 }
101 }
102
103 // Let urlPattern be a new URL pattern.
104 url_pattern<regex_provider> url_pattern_{};
105
106 // Set urlPattern's protocol component to the result of compiling a component
107 // given processedInit["protocol"], canonicalize a protocol, and default
108 // options.
109 auto protocol_component = url_pattern_component<regex_provider>::compile(
110 processed_init->protocol.value(),
111 url_pattern_helpers::canonicalize_protocol,
112 url_pattern_compile_component_options::DEFAULT);
113 if (!protocol_component) {
114 ada_log("url_pattern_component::compile failed for protocol ",
115 processed_init->protocol.value());
116 return tl::unexpected(protocol_component.error());
117 }
118 url_pattern_.protocol_component = std::move(*protocol_component);
119
120 // Set urlPattern's username component to the result of compiling a component
121 // given processedInit["username"], canonicalize a username, and default
122 // options.
123 auto username_component = url_pattern_component<regex_provider>::compile(
124 processed_init->username.value(),
125 url_pattern_helpers::canonicalize_username,
126 url_pattern_compile_component_options::DEFAULT);
127 if (!username_component) {
128 ada_log("url_pattern_component::compile failed for username ",
129 processed_init->username.value());
130 return tl::unexpected(username_component.error());
131 }
132 url_pattern_.username_component = std::move(*username_component);
133
134 // Set urlPattern's password component to the result of compiling a component
135 // given processedInit["password"], canonicalize a password, and default
136 // options.
137 auto password_component = url_pattern_component<regex_provider>::compile(
138 processed_init->password.value(),
139 url_pattern_helpers::canonicalize_password,
140 url_pattern_compile_component_options::DEFAULT);
141 if (!password_component) {
142 ada_log("url_pattern_component::compile failed for password ",
143 processed_init->password.value());
144 return tl::unexpected(password_component.error());
145 }
146 url_pattern_.password_component = std::move(*password_component);
147
148 // TODO: Optimization opportunity. The following if statement can be
149 // simplified.
150 // If the result running hostname pattern is an IPv6 address given
151 // processedInit["hostname"] is true, then set urlPattern's hostname component
152 // to the result of compiling a component given processedInit["hostname"],
153 // canonicalize an IPv6 hostname, and hostname options.
154 if (url_pattern_helpers::is_ipv6_address(processed_init->hostname.value())) {
155 ada_log("processed_init->hostname is ipv6 address");
156 // then set urlPattern's hostname component to the result of compiling a
157 // component given processedInit["hostname"], canonicalize an IPv6 hostname,
158 // and hostname options.
159 auto hostname_component = url_pattern_component<regex_provider>::compile(
160 processed_init->hostname.value(),
161 url_pattern_helpers::canonicalize_ipv6_hostname,
162 url_pattern_compile_component_options::DEFAULT);
163 if (!hostname_component) {
164 ada_log("url_pattern_component::compile failed for ipv6 hostname ",
165 processed_init->hostname.value());
166 return tl::unexpected(hostname_component.error());
167 }
168 url_pattern_.hostname_component = std::move(*hostname_component);
169 } else {
170 // Otherwise, set urlPattern's hostname component to the result of compiling
171 // a component given processedInit["hostname"], canonicalize a hostname, and
172 // hostname options.
173 auto hostname_component = url_pattern_component<regex_provider>::compile(
174 processed_init->hostname.value(),
175 url_pattern_helpers::canonicalize_hostname,
176 url_pattern_compile_component_options::HOSTNAME);
177 if (!hostname_component) {
178 ada_log("url_pattern_component::compile failed for hostname ",
179 processed_init->hostname.value());
180 return tl::unexpected(hostname_component.error());
181 }
182 url_pattern_.hostname_component = std::move(*hostname_component);
183 }
184
185 // Set urlPattern's port component to the result of compiling a component
186 // given processedInit["port"], canonicalize a port, and default options.
187 auto port_component = url_pattern_component<regex_provider>::compile(
188 processed_init->port.value(), url_pattern_helpers::canonicalize_port,
189 url_pattern_compile_component_options::DEFAULT);
190 if (!port_component) {
191 ada_log("url_pattern_component::compile failed for port ",
192 processed_init->port.value());
193 return tl::unexpected(port_component.error());
194 }
195 url_pattern_.port_component = std::move(*port_component);
196
197 // Let compileOptions be a copy of the default options with the ignore case
198 // property set to options["ignoreCase"].
199 auto compile_options = url_pattern_compile_component_options::DEFAULT;
200 if (options) {
201 compile_options.ignore_case = options->ignore_case;
202 }
203
204 // TODO: Optimization opportunity: Simplify this if statement.
205 // If the result of running protocol component matches a special scheme given
206 // urlPattern's protocol component is true, then:
207 if (url_pattern_helpers::protocol_component_matches_special_scheme<
208 regex_provider>(url_pattern_.protocol_component)) {
209 // Let pathCompileOptions be copy of the pathname options with the ignore
210 // case property set to options["ignoreCase"].
211 auto path_compile_options = url_pattern_compile_component_options::PATHNAME;
212 if (options) {
213 path_compile_options.ignore_case = options->ignore_case;
214 }
215
216 // Set urlPattern's pathname component to the result of compiling a
217 // component given processedInit["pathname"], canonicalize a pathname, and
218 // pathCompileOptions.
219 auto pathname_component = url_pattern_component<regex_provider>::compile(
220 processed_init->pathname.value(),
221 url_pattern_helpers::canonicalize_pathname, path_compile_options);
222 if (!pathname_component) {
223 ada_log("url_pattern_component::compile failed for pathname ",
224 processed_init->pathname.value());
225 return tl::unexpected(pathname_component.error());
226 }
227 url_pattern_.pathname_component = std::move(*pathname_component);
228 } else {
229 // Otherwise set urlPattern's pathname component to the result of compiling
230 // a component given processedInit["pathname"], canonicalize an opaque
231 // pathname, and compileOptions.
232 auto pathname_component = url_pattern_component<regex_provider>::compile(
233 processed_init->pathname.value(),
234 url_pattern_helpers::canonicalize_opaque_pathname, compile_options);
235 if (!pathname_component) {
236 ada_log("url_pattern_component::compile failed for opaque pathname ",
237 processed_init->pathname.value());
238 return tl::unexpected(pathname_component.error());
239 }
240 url_pattern_.pathname_component = std::move(*pathname_component);
241 }
242
243 // Set urlPattern's search component to the result of compiling a component
244 // given processedInit["search"], canonicalize a search, and compileOptions.
245 auto search_component = url_pattern_component<regex_provider>::compile(
246 processed_init->search.value(), url_pattern_helpers::canonicalize_search,
247 compile_options);
248 if (!search_component) {
249 ada_log("url_pattern_component::compile failed for search ",
250 processed_init->search.value());
251 return tl::unexpected(search_component.error());
252 }
253 url_pattern_.search_component = std::move(*search_component);
254
255 // Set urlPattern's hash component to the result of compiling a component
256 // given processedInit["hash"], canonicalize a hash, and compileOptions.
257 auto hash_component = url_pattern_component<regex_provider>::compile(
258 processed_init->hash.value(), url_pattern_helpers::canonicalize_hash,
259 compile_options);
260 if (!hash_component) {
261 ada_log("url_pattern_component::compile failed for hash ",
262 processed_init->hash.value());
263 return tl::unexpected(hash_component.error());
264 }
265 url_pattern_.hash_component = std::move(*hash_component);
266
267 // Return urlPattern.
268 return url_pattern_;
269}
270#endif // ADA_INCLUDE_URL_PATTERN
271
272} // namespace ada::parser
273
274#endif // ADA_PARSER_INL_H
#define ADA_ASSERT_TRUE(COND)
Internal URL parsing implementation.
Definition parser-inl.h:16
constexpr uint16_t get_special_port(std::string_view scheme) noexcept
Definition scheme-inl.h:57
errors
Error codes for URL parsing operations.
Definition errors.h:17
@ type_error
Definition errors.h:18
Low-level URL parsing functions.
ada::url_pattern_regex::std_regex_provider regex_provider
Definition url_pattern.cc:9
URLPattern API implementation.
Declaration for the URLPattern helpers.