Skip to content

Commit 549fefb

Browse files
committed
[WIP] Implement the URL Pattern WHATWG specification
See: https://urlpattern.spec.whatwg.org Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent b21704e commit 549fefb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+14170
-0
lines changed

.github/workflows/website-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ jobs:
2929
-DSOURCEMETA_CORE_JSONSCHEMA:BOOL=OFF
3030
-DSOURCEMETA_CORE_JSONPOINTER:BOOL=OFF
3131
-DSOURCEMETA_CORE_YAML:BOOL=OFF
32+
-DSOURCEMETA_CORE_URLPATTERN:BOOL=OFF
3233
-DSOURCEMETA_CORE_EXTENSION_ALTERSCHEMA:BOOL=OFF
3334
-DSOURCEMETA_CORE_EXTENSION_EDITORSCHEMA:BOOL=OFF
3435
-DSOURCEMETA_CORE_EXTENSION_SCHEMACONFIG:BOOL=OFF

.github/workflows/website-deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
-DSOURCEMETA_CORE_JSONSCHEMA:BOOL=OFF
4040
-DSOURCEMETA_CORE_JSONPOINTER:BOOL=OFF
4141
-DSOURCEMETA_CORE_YAML:BOOL=OFF
42+
-DSOURCEMETA_CORE_URLPATTERN:BOOL=OFF
4243
-DSOURCEMETA_CORE_EXTENSION_ALTERSCHEMA:BOOL=OFF
4344
-DSOURCEMETA_CORE_EXTENSION_EDITORSCHEMA:BOOL=OFF
4445
-DSOURCEMETA_CORE_EXTENSION_SCHEMACONFIG:BOOL=OFF

CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ option(SOURCEMETA_CORE_JSONSCHEMA "Build the Sourcemeta Core JSON Schema library
1717
option(SOURCEMETA_CORE_JSONPOINTER "Build the Sourcemeta Core JSON Pointer library" ON)
1818
option(SOURCEMETA_CORE_JSONL "Build the Sourcemeta Core JSONL library" ON)
1919
option(SOURCEMETA_CORE_YAML "Build the Sourcemeta Core YAML library" ON)
20+
option(SOURCEMETA_CORE_URLPATTERN "Build the Sourcemeta Core URL Pattern library" ON)
2021
option(SOURCEMETA_CORE_EXTENSION_ALTERSCHEMA "Build the Sourcemeta Core AlterSchema library" ON)
2122
option(SOURCEMETA_CORE_EXTENSION_EDITORSCHEMA "Build the Sourcemeta Core EditorSchema library" ON)
2223
option(SOURCEMETA_CORE_EXTENSION_SCHEMACONFIG "Build the Sourcemeta Core SchemaConfig library" ON)
@@ -116,6 +117,10 @@ if(SOURCEMETA_CORE_YAML)
116117
add_subdirectory(src/core/yaml)
117118
endif()
118119

120+
if(SOURCEMETA_CORE_URLPATTERN)
121+
add_subdirectory(src/core/urlpattern)
122+
endif()
123+
119124
if(SOURCEMETA_CORE_EXTENSION_ALTERSCHEMA)
120125
add_subdirectory(src/extension/alterschema)
121126
endif()
@@ -223,6 +228,10 @@ if(SOURCEMETA_CORE_TESTS)
223228
add_subdirectory(test/yaml)
224229
endif()
225230

231+
if(SOURCEMETA_CORE_URLPATTERN)
232+
add_subdirectory(test/urlpattern)
233+
endif()
234+
226235
if(SOURCEMETA_CORE_EXTENSION_ALTERSCHEMA)
227236
add_subdirectory(test/alterschema)
228237
endif()

DEPENDENCIES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ yaml https://github.com/yaml/libyaml 0.2.5
1515
pcre2 https://github.com/PCRE2Project/pcre2 pcre2-10.47
1616
googletest https://github.com/google/googletest a7f443b80b105f940225332ed3c31f2790092f47
1717
googlebenchmark https://github.com/google/benchmark 378fe693a1ef51500db21b11ff05a8018c5f0e55
18+
wpt https://github.com/web-platform-tests/wpt 71154f49e6cdda82c43e11bb74cfe3ad7b3f9368

config.cmake.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ if(NOT SOURCEMETA_CORE_COMPONENTS)
1818
list(APPEND SOURCEMETA_CORE_COMPONENTS jsonpointer)
1919
list(APPEND SOURCEMETA_CORE_COMPONENTS jsonschema)
2020
list(APPEND SOURCEMETA_CORE_COMPONENTS yaml)
21+
list(APPEND SOURCEMETA_CORE_COMPONENTS urlpattern)
2122
list(APPEND SOURCEMETA_CORE_COMPONENTS alterschema)
2223
list(APPEND SOURCEMETA_CORE_COMPONENTS editorschema)
2324
list(APPEND SOURCEMETA_CORE_COMPONENTS schemaconfig)
@@ -84,6 +85,10 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS})
8485
find_dependency(yaml CONFIG)
8586
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake")
8687
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_yaml.cmake")
88+
elseif(component STREQUAL "urlpattern")
89+
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake")
90+
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_regex.cmake")
91+
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_urlpattern.cmake")
8792
elseif(component STREQUAL "alterschema")
8893
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake")
8994
find_dependency(mpdecimal CONFIG)

src/core/regex/include/sourcemeta/core/regex.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ using RegexTypePrefix = std::string;
3030
/// @ingroup regex
3131
struct RegexTypeNonEmpty {
3232
auto operator==(const RegexTypeNonEmpty &) const noexcept -> bool = default;
33+
auto operator<(const RegexTypeNonEmpty &) const noexcept -> bool {
34+
return false;
35+
}
3336
};
3437

3538
/// @ingroup regex
@@ -41,11 +44,15 @@ struct RegexTypePCRE2 {
4144
auto operator==(const RegexTypePCRE2 &other) const noexcept -> bool {
4245
return this->code == other.code;
4346
}
47+
auto operator<(const RegexTypePCRE2 &other) const noexcept -> bool {
48+
return this->code < other.code;
49+
}
4450
};
4551

4652
/// @ingroup regex
4753
struct RegexTypeNoop {
4854
auto operator==(const RegexTypeNoop &) const noexcept -> bool = default;
55+
auto operator<(const RegexTypeNoop &) const noexcept -> bool { return false; }
4956
};
5057

5158
/// @ingroup regex

src/core/urlpattern/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME urlpattern
2+
PRIVATE_HEADERS error.h part.h
3+
SOURCES urlpattern.cc urlpattern_part.cc)
4+
5+
if(SOURCEMETA_CORE_INSTALL)
6+
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME urlpattern)
7+
endif()
8+
9+
target_link_libraries(sourcemeta_core_urlpattern PUBLIC
10+
sourcemeta::core::json)
11+
target_link_libraries(sourcemeta_core_urlpattern PUBLIC
12+
sourcemeta::core::regex)
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#ifndef SOURCEMETA_CORE_URLPATTERN_H_
2+
#define SOURCEMETA_CORE_URLPATTERN_H_
3+
4+
#ifndef SOURCEMETA_CORE_URLPATTERN_EXPORT
5+
#include <sourcemeta/core/urlpattern_export.h>
6+
#endif
7+
8+
// NOLINTBEGIN(misc-include-cleaner)
9+
#include <sourcemeta/core/urlpattern_error.h>
10+
#include <sourcemeta/core/urlpattern_part.h>
11+
// NOLINTEND(misc-include-cleaner)
12+
13+
#include <compare> // std::strong_ordering
14+
#include <optional> // std::optional
15+
#include <string> // std::string
16+
#include <string_view> // std::string_view
17+
#include <unordered_map> // std::unordered_map
18+
#include <utility> // std::move
19+
#include <vector> // std::vector
20+
21+
/// @defgroup urlpattern URL Pattern
22+
/// @brief A WHATWG URL Pattern implementation.
23+
///
24+
/// This functionality is included as follows:
25+
///
26+
/// ```cpp
27+
/// #include <sourcemeta/core/urlpattern.h>
28+
/// ```
29+
30+
namespace sourcemeta::core {
31+
32+
/// @ingroup urlpattern
33+
struct URLPatternComponentResult {
34+
[[nodiscard]] auto size() const noexcept -> std::size_t;
35+
[[nodiscard]] auto at(const std::size_t index) const noexcept
36+
-> std::string_view;
37+
[[nodiscard]] auto at(const std::string_view name) const noexcept
38+
-> std::optional<std::string_view>;
39+
40+
auto insert(const std::string_view value) -> void;
41+
auto insert(const std::string_view name, const std::string_view value)
42+
-> void;
43+
auto insert(const std::string_view name, const std::size_t index) -> void;
44+
45+
private:
46+
std::vector<std::string_view> positions;
47+
std::unordered_map<std::string_view, std::size_t> names;
48+
};
49+
50+
/// @ingroup urlpattern
51+
struct URLPatternProtocol {
52+
URLPatternProtocol() : value{URLPatternPartAsterisk{}} {}
53+
URLPatternProtocol(const char *input);
54+
URLPatternProtocol(const URLPatternProtocol &) = default;
55+
URLPatternProtocol(URLPatternProtocol &&) noexcept = default;
56+
auto operator=(const URLPatternProtocol &) -> URLPatternProtocol & = default;
57+
auto operator=(URLPatternProtocol &&) noexcept
58+
-> URLPatternProtocol & = default;
59+
60+
auto operator==(const URLPatternProtocol &other) const -> bool = default;
61+
auto operator<=>(const URLPatternProtocol &other) const
62+
-> std::strong_ordering;
63+
64+
URLPatternPart value;
65+
[[nodiscard]] auto match(const std::string_view input) const
66+
-> std::optional<URLPatternComponentResult>;
67+
};
68+
69+
/// @ingroup urlpattern
70+
struct URLPatternUsername {
71+
URLPatternUsername() : value{URLPatternPartAsterisk{}} {}
72+
URLPatternUsername(const char *input);
73+
URLPatternUsername(const URLPatternUsername &) = default;
74+
URLPatternUsername(URLPatternUsername &&) noexcept = default;
75+
auto operator=(const URLPatternUsername &) -> URLPatternUsername & = default;
76+
auto operator=(URLPatternUsername &&) noexcept
77+
-> URLPatternUsername & = default;
78+
79+
auto operator==(const URLPatternUsername &other) const -> bool = default;
80+
auto operator<=>(const URLPatternUsername &other) const
81+
-> std::strong_ordering;
82+
83+
URLPatternPart value;
84+
[[nodiscard]] auto match(const std::string_view input) const
85+
-> std::optional<URLPatternComponentResult>;
86+
};
87+
88+
/// @ingroup urlpattern
89+
struct URLPatternPassword {
90+
URLPatternPassword() : value{URLPatternPartAsterisk{}} {}
91+
URLPatternPassword(const char *input);
92+
URLPatternPassword(const URLPatternPassword &) = default;
93+
URLPatternPassword(URLPatternPassword &&) noexcept = default;
94+
auto operator=(const URLPatternPassword &) -> URLPatternPassword & = default;
95+
auto operator=(URLPatternPassword &&) noexcept
96+
-> URLPatternPassword & = default;
97+
98+
auto operator==(const URLPatternPassword &other) const -> bool = default;
99+
auto operator<=>(const URLPatternPassword &other) const
100+
-> std::strong_ordering;
101+
102+
URLPatternPart value;
103+
[[nodiscard]] auto match(const std::string_view input) const
104+
-> std::optional<URLPatternComponentResult>;
105+
};
106+
107+
/// @ingroup urlpattern
108+
struct URLPatternHostname {
109+
URLPatternHostname() : value{{URLPatternPartAsterisk{}}} {}
110+
URLPatternHostname(const char *input);
111+
URLPatternHostname(const URLPatternHostname &) = default;
112+
URLPatternHostname(URLPatternHostname &&) noexcept = default;
113+
auto operator=(const URLPatternHostname &) -> URLPatternHostname & = default;
114+
auto operator=(URLPatternHostname &&) noexcept
115+
-> URLPatternHostname & = default;
116+
117+
auto operator==(const URLPatternHostname &other) const -> bool = default;
118+
auto operator<=>(const URLPatternHostname &other) const
119+
-> std::strong_ordering;
120+
121+
std::vector<URLPatternPart> value;
122+
[[nodiscard]] auto match(const std::string_view input) const
123+
-> std::optional<URLPatternComponentResult>;
124+
};
125+
126+
/// @ingroup urlpattern
127+
struct URLPatternPort {
128+
URLPatternPort() : value{URLPatternPartAsterisk{}} {}
129+
URLPatternPort(const char *input);
130+
URLPatternPort(const URLPatternPort &) = default;
131+
URLPatternPort(URLPatternPort &&) noexcept = default;
132+
auto operator=(const URLPatternPort &) -> URLPatternPort & = default;
133+
auto operator=(URLPatternPort &&) noexcept -> URLPatternPort & = default;
134+
135+
auto operator==(const URLPatternPort &other) const -> bool = default;
136+
auto operator<=>(const URLPatternPort &other) const -> std::strong_ordering;
137+
138+
URLPatternPart value;
139+
[[nodiscard]] auto match(const std::string_view input) const
140+
-> std::optional<URLPatternComponentResult>;
141+
};
142+
143+
/// @ingroup urlpattern
144+
struct URLPatternPathname {
145+
URLPatternPathname() : value{{URLPatternPartAsterisk{}}} {}
146+
URLPatternPathname(const char *input);
147+
URLPatternPathname(const URLPatternPathname &) = default;
148+
URLPatternPathname(URLPatternPathname &&) noexcept = default;
149+
auto operator=(const URLPatternPathname &) -> URLPatternPathname & = default;
150+
auto operator=(URLPatternPathname &&) noexcept
151+
-> URLPatternPathname & = default;
152+
153+
auto operator==(const URLPatternPathname &other) const -> bool = default;
154+
auto operator<=>(const URLPatternPathname &other) const
155+
-> std::strong_ordering;
156+
157+
std::vector<URLPatternPart> value;
158+
// TODO: Find a way to get rid of this
159+
bool is_bare_pattern{false};
160+
[[nodiscard]] auto match(const std::string_view input) const
161+
-> std::optional<URLPatternComponentResult>;
162+
};
163+
164+
/// @ingroup urlpattern
165+
struct URLPatternSearch {
166+
URLPatternSearch() : value{URLPatternPartAsterisk{}} {}
167+
URLPatternSearch(const char *input);
168+
URLPatternSearch(const URLPatternSearch &) = default;
169+
URLPatternSearch(URLPatternSearch &&) noexcept = default;
170+
auto operator=(const URLPatternSearch &) -> URLPatternSearch & = default;
171+
auto operator=(URLPatternSearch &&) noexcept -> URLPatternSearch & = default;
172+
173+
auto operator==(const URLPatternSearch &other) const -> bool = default;
174+
auto operator<=>(const URLPatternSearch &other) const -> std::strong_ordering;
175+
176+
// URL Pattern treats search queries as opaque strings
177+
URLPatternPart value;
178+
[[nodiscard]] auto match(const std::string_view input) const
179+
-> std::optional<URLPatternComponentResult>;
180+
};
181+
182+
/// @ingroup urlpattern
183+
struct URLPatternHash {
184+
URLPatternHash() : value{URLPatternPartAsterisk{}} {}
185+
URLPatternHash(const char *input);
186+
URLPatternHash(const URLPatternHash &) = default;
187+
URLPatternHash(URLPatternHash &&) noexcept = default;
188+
auto operator=(const URLPatternHash &) -> URLPatternHash & = default;
189+
auto operator=(URLPatternHash &&) noexcept -> URLPatternHash & = default;
190+
191+
auto operator==(const URLPatternHash &other) const -> bool = default;
192+
auto operator<=>(const URLPatternHash &other) const -> std::strong_ordering;
193+
194+
URLPatternPart value;
195+
[[nodiscard]] auto match(const std::string_view input) const
196+
-> std::optional<URLPatternComponentResult>;
197+
};
198+
199+
/// @ingroup urlpattern
200+
struct URLPatternResult {
201+
std::optional<URLPatternComponentResult> protocol;
202+
std::optional<URLPatternComponentResult> username;
203+
std::optional<URLPatternComponentResult> password;
204+
std::optional<URLPatternComponentResult> hostname;
205+
std::optional<URLPatternComponentResult> port;
206+
std::optional<URLPatternComponentResult> pathname;
207+
std::optional<URLPatternComponentResult> search;
208+
std::optional<URLPatternComponentResult> hash;
209+
};
210+
211+
/// @ingroup urlpattern
212+
struct URLPatternInput {
213+
std::string_view protocol;
214+
std::string_view username;
215+
std::string_view password;
216+
std::string_view hostname;
217+
std::string_view port;
218+
std::string_view pathname;
219+
std::string_view search;
220+
std::string_view hash;
221+
};
222+
223+
/// @ingroup urlpattern
224+
/// See https://urlpattern.spec.whatwg.org/#url-pattern-struct
225+
struct URLPattern {
226+
URLPatternProtocol protocol;
227+
URLPatternUsername username;
228+
URLPatternPassword password;
229+
URLPatternHostname hostname;
230+
URLPatternPort port;
231+
URLPatternPathname pathname;
232+
URLPatternSearch search;
233+
URLPatternHash hash;
234+
235+
[[nodiscard]] static auto parse(const std::string_view input) -> URLPattern;
236+
237+
[[nodiscard]] auto match(const URLPatternInput &input) const
238+
-> URLPatternResult;
239+
};
240+
241+
} // namespace sourcemeta::core
242+
243+
#endif
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef SOURCEMETA_CORE_URLPATTERN_ERROR_H_
2+
#define SOURCEMETA_CORE_URLPATTERN_ERROR_H_
3+
4+
#ifndef SOURCEMETA_CORE_URLPATTERN_EXPORT
5+
#include <sourcemeta/core/urlpattern_export.h>
6+
#endif
7+
8+
#include <cstdint> // std::uint64_t
9+
#include <exception> // std::exception
10+
11+
namespace sourcemeta::core {
12+
13+
// Exporting symbols that depends on the standard C++ library is considered
14+
// safe.
15+
// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN
16+
#if defined(_MSC_VER)
17+
#pragma warning(disable : 4251 4275)
18+
#endif
19+
20+
/// @ingroup urlpattern
21+
/// An error that represents a URL Pattern failure
22+
class SOURCEMETA_CORE_URLPATTERN_EXPORT URLPatternParseError
23+
: public std::exception {
24+
public:
25+
URLPatternParseError(const std::uint64_t column) : column_{column} {}
26+
27+
[[nodiscard]] auto what() const noexcept -> const char * override {
28+
return "The input is not a valid URL Pattern";
29+
}
30+
31+
/// Get the column number of the error
32+
[[nodiscard]] auto column() const noexcept -> std::uint64_t {
33+
return column_;
34+
}
35+
36+
private:
37+
std::uint64_t column_;
38+
};
39+
40+
#if defined(_MSC_VER)
41+
#pragma warning(default : 4251 4275)
42+
#endif
43+
44+
} // namespace sourcemeta::core
45+
46+
#endif

0 commit comments

Comments
 (0)