|
|
@@ -34,6 +34,7 @@ bool date_time(std::string_view dt);
|
|
|
bool duration(std::string_view dur);
|
|
|
|
|
|
template <typename CharT = char> bool uri(std::basic_string_view<CharT> uri);
|
|
|
+template <typename CharT = char> bool uri_reference(std::basic_string_view<CharT> uri);
|
|
|
bool uuid(std::string_view id);
|
|
|
template <typename CharT = char> bool hostname(std::basic_string_view<CharT> name);
|
|
|
|
|
|
@@ -85,6 +86,7 @@ inline bool is_leapsecond(std::tm tm) {
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+// https://www.rfc-editor.org/rfc/rfc6570.html#section-1.5
|
|
|
inline bool is_uschar(int c) {
|
|
|
using P = std::pair<int, int>;
|
|
|
constexpr std::array data{
|
|
|
@@ -137,6 +139,19 @@ template <typename CharT> inline bool is_uri_authority(std::basic_string_view<Ch
|
|
|
}
|
|
|
return ipv4(to_u8(uri)) || hostname(uri);
|
|
|
}
|
|
|
+
|
|
|
+template <typename CharT> bool test_uri_part(std::basic_string_view<CharT> & uri, char delim) {
|
|
|
+ size_t const pos = uri.find(delim);
|
|
|
+ if (pos == uri.npos) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ auto part = uri.substr(pos + 1);
|
|
|
+ uri = uri.substr(0, pos);
|
|
|
+ for (size_t pos = 0; pos < part.size(); ++pos) {
|
|
|
+ RETURN_UNLESS(detail::is_pchar(part, pos, ":@/?"), false);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+};
|
|
|
}
|
|
|
|
|
|
namespace jvalidate::format {
|
|
|
@@ -182,18 +197,6 @@ inline bool date_time(std::string_view dt) {
|
|
|
|
|
|
template <typename CharT> inline bool uri(std::basic_string_view<CharT> uri) {
|
|
|
using delim = detail::char_delimiters<CharT>;
|
|
|
- auto test_uri_part = [&uri](char delim) {
|
|
|
- size_t const pos = uri.find(delim);
|
|
|
- if (pos == uri.npos) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- auto part = uri.substr(pos + 1);
|
|
|
- uri = uri.substr(0, pos);
|
|
|
- for (size_t pos = 0; pos < part.size(); ++pos) {
|
|
|
- RETURN_UNLESS(detail::is_pchar(part, pos, ":@/?"), false);
|
|
|
- }
|
|
|
- return true;
|
|
|
- };
|
|
|
|
|
|
// https://www.rfc-editor.org/rfc/rfc3986.html#appendix-A
|
|
|
if (size_t const pos = uri.find(':'); pos != uri.npos) {
|
|
|
@@ -206,8 +209,32 @@ template <typename CharT> inline bool uri(std::basic_string_view<CharT> uri) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- RETURN_UNLESS(test_uri_part('#'), false);
|
|
|
- RETURN_UNLESS(test_uri_part('?'), false);
|
|
|
+ RETURN_UNLESS(detail::test_uri_part(uri, '#'), false);
|
|
|
+ RETURN_UNLESS(detail::test_uri_part(uri, '?'), false);
|
|
|
+
|
|
|
+ auto path = uri;
|
|
|
+ if (uri.starts_with(delim::double_slash)) {
|
|
|
+ uri.remove_prefix(2);
|
|
|
+ path = uri.substr(std::min(uri.size(), uri.find('/')));
|
|
|
+ uri.remove_suffix(path.size());
|
|
|
+ RETURN_UNLESS(detail::is_uri_authority(uri), false);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < path.size(); ++i) {
|
|
|
+ RETURN_UNLESS(detail::is_pchar(path, i, "/:@"), false);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename CharT> inline bool uri_reference(std::basic_string_view<CharT> uri) {
|
|
|
+ using delim = detail::char_delimiters<CharT>;
|
|
|
+ if (jvalidate::format::uri(uri)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ RETURN_UNLESS(detail::test_uri_part(uri, '#'), false);
|
|
|
+ RETURN_UNLESS(detail::test_uri_part(uri, '?'), false);
|
|
|
|
|
|
auto path = uri;
|
|
|
if (uri.starts_with(delim::double_slash)) {
|
|
|
@@ -491,13 +518,13 @@ private:
|
|
|
{"ipv4", &format::ipv4},
|
|
|
{"ipv6", &format::ipv6},
|
|
|
{"iri", UTF32(uri)},
|
|
|
- {"iri-reference", nullptr},
|
|
|
+ {"iri-reference", UTF32(uri_reference)},
|
|
|
{"json-pointer", CONSTRUCTS(Pointer)},
|
|
|
{"relative-json-pointer", CONSTRUCTS(RelativePointer)},
|
|
|
{"regex", nullptr},
|
|
|
{"time", &format::time},
|
|
|
{"uri", &format::uri},
|
|
|
- {"uri-reference", nullptr},
|
|
|
+ {"uri-reference", &format::uri_reference},
|
|
|
{"uri-template", nullptr},
|
|
|
{"uuid", &format::uuid},
|
|
|
};
|