|
|
@@ -35,6 +35,7 @@ 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 uri_template(std::u32string_view uri);
|
|
|
bool uuid(std::string_view id);
|
|
|
template <typename CharT = char> bool hostname(std::basic_string_view<CharT> name);
|
|
|
|
|
|
@@ -109,11 +110,62 @@ inline bool is_pchar(std::basic_string_view<CharT> part, size_t & pos,
|
|
|
return true;
|
|
|
}
|
|
|
if (part[pos] == '%') {
|
|
|
- return std::strchr(g_hex_digits, part[++pos]) && std::strchr(g_hex_digits, part[++pos]);
|
|
|
+ return pos + 2 < part.size() && std::strchr(g_hex_digits, part[++pos]) &&
|
|
|
+ std::strchr(g_hex_digits, part[++pos]);
|
|
|
}
|
|
|
return extra_valid_chars.find(part[pos]) != part.npos;
|
|
|
};
|
|
|
|
|
|
+inline bool is_uri_template_literal(std::u32string_view part, size_t & pos) {
|
|
|
+ constexpr char const * g_hex_digits = "0123456789ABCDEFabcdef";
|
|
|
+ if (part[pos] == '%') {
|
|
|
+ return pos + 2 < part.size() && std::strchr(g_hex_digits, part[++pos]) &&
|
|
|
+ std::strchr(g_hex_digits, part[++pos]);
|
|
|
+ }
|
|
|
+ return !std::strchr(R"( "'%<>\^`{|}`)", part[pos]) && part[pos] > 0x1F && part[pos] != 0x7F;
|
|
|
+}
|
|
|
+
|
|
|
+inline bool is_uri_template_varchar(std::u32string_view part, size_t & pos) {
|
|
|
+ constexpr char const * g_hex_digits = "0123456789ABCDEFabcdef";
|
|
|
+ if (part[pos] == '%') {
|
|
|
+ return pos + 2 < part.size() && std::strchr(g_hex_digits, part[++pos]) &&
|
|
|
+ std::strchr(g_hex_digits, part[++pos]);
|
|
|
+ }
|
|
|
+ return std::isalnum(part[pos]) || part[pos] == '_';
|
|
|
+}
|
|
|
+
|
|
|
+inline bool is_uri_template_expression(std::u32string_view part) {
|
|
|
+ if (part.empty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (std::strchr("+#./;?&=,!@|", part[0])) {
|
|
|
+ part.remove_prefix(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t pos = part.find(','); !part.empty();
|
|
|
+ part.remove_prefix(std::min(part.size(), pos)), pos = part.find(',')) {
|
|
|
+ std::u32string_view varspec = part.substr(0, pos);
|
|
|
+ std::u32string_view expand;
|
|
|
+ if (size_t const mod = varspec.find_first_of(U":*"); mod != varspec.npos) {
|
|
|
+ expand = varspec.substr(mod + 1);
|
|
|
+ varspec.remove_suffix(expand.size() + 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (expand.empty() || expand == U"*") {
|
|
|
+ // No Modifier, or Explode
|
|
|
+ } else if (expand.size() > 4 || expand[0] == '0' ||
|
|
|
+ not std::ranges::all_of(expand, [](char c) { return std::isdigit(c); })) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for (size_t i = 0; i < varspec.size(); ++i) {
|
|
|
+ RETURN_UNLESS(is_uri_template_varchar(varspec, i) || (i > 0 && varspec[i] == '.'), false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
template <typename CharT> inline bool is_uri_authority(std::basic_string_view<CharT> uri) {
|
|
|
if (size_t pos = uri.find('@'); pos != uri.npos && pos < uri.find('/')) {
|
|
|
for (size_t i = 0; i < pos; ++i) {
|
|
|
@@ -258,6 +310,22 @@ template <typename CharT> inline bool uri_reference(std::basic_string_view<CharT
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+inline bool uri_template(std::u32string_view uri) {
|
|
|
+ for (size_t i = 0; i < uri.size(); ++i) {
|
|
|
+ if (uri[i] != '{') {
|
|
|
+ RETURN_UNLESS(detail::is_uri_template_literal(uri, i), false);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::u32string_view expr = uri.substr(i + 1);
|
|
|
+ size_t const pos = expr.find('}');
|
|
|
+ RETURN_UNLESS(pos != uri.npos, false);
|
|
|
+ RETURN_UNLESS(detail::is_uri_template_expression(expr.substr(0, pos)), false);
|
|
|
+ i += pos + 1;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
inline bool uuid(std::string_view id) {
|
|
|
constexpr char const * g_hex_digits = "0123456789ABCDEFabcdef";
|
|
|
constexpr size_t g_uuid_len = 36;
|
|
|
@@ -525,7 +593,7 @@ private:
|
|
|
{"time", &format::time},
|
|
|
{"uri", &format::uri},
|
|
|
{"uri-reference", &format::uri_reference},
|
|
|
- {"uri-template", nullptr},
|
|
|
+ {"uri-template", &format::utf32<format::uri_template>},
|
|
|
{"uuid", &format::uuid},
|
|
|
};
|
|
|
|