From b0c7fee5ba3907e1045c67715e59dd0feebe039c Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Thu, 14 May 2026 16:28:48 -0400 Subject: [PATCH] Add a `URITemplate::is_uritemplate` static method Signed-off-by: Juan Cruz Viotti --- src/core/uritemplate/helpers.h | 54 ++++++++---- .../include/sourcemeta/core/uritemplate.h | 15 ++++ src/core/uritemplate/uritemplate.cc | 51 +++++++++--- .../uritemplate_parse_error_test.cc | 3 +- test/uritemplate/uritemplate_parse_test.cc | 82 +++++++++++++++++++ 5 files changed, 176 insertions(+), 29 deletions(-) diff --git a/src/core/uritemplate/helpers.h b/src/core/uritemplate/helpers.h index 5b84b56bd..a071ed5a0 100644 --- a/src/core/uritemplate/helpers.h +++ b/src/core/uritemplate/helpers.h @@ -177,9 +177,10 @@ inline auto parse_varname(const std::string_view input, std::size_t position) return position; } +template inline auto parse_variable_list(const std::string_view input, std::size_t position, - std::vector &variables) + std::vector *variables) -> std::size_t { while (true) { const auto start = position; @@ -189,9 +190,9 @@ parse_variable_list(const std::string_view input, std::size_t position, throw URITemplateParseError(position + 1); } - const auto name = input.substr(start, position - start); - std::uint16_t length = 0; - bool explode = false; + [[maybe_unused]] const auto name = input.substr(start, position - start); + [[maybe_unused]] std::uint16_t length = 0; + [[maybe_unused]] bool explode = false; if (position >= input.size()) { throw URITemplateParseError(1); @@ -225,14 +226,20 @@ parse_variable_list(const std::string_view input, std::size_t position, throw URITemplateParseError(prefix_start + 1); } - length = value; + if constexpr (!CheckOnly) { + length = value; + } } else if (input[position] == '*') { - explode = true; + if constexpr (!CheckOnly) { + explode = true; + } position++; } - variables.push_back(URITemplateVariableSpecification{ - .name = name, .length = length, .explode = explode}); + if constexpr (!CheckOnly) { + variables->push_back(URITemplateVariableSpecification{ + .name = name, .length = length, .explode = explode}); + } if (position >= input.size()) { throw URITemplateParseError(1); @@ -250,8 +257,10 @@ parse_variable_list(const std::string_view input, std::size_t position, return position; } -template -auto parse_expression(const std::string_view input) -> URITemplateParseResult { +template +auto parse_expression(const std::string_view input) + -> std::conditional_t, + URITemplateParseResult> { if constexpr (std::is_same_v) { if (input.empty() || input[0] == '{') { return std::nullopt; @@ -272,9 +281,13 @@ auto parse_expression(const std::string_view input) -> URITemplateParseResult { position++; } - return std::make_pair( - URITemplateToken{URITemplateTokenLiteral{input.substr(0, position)}}, - position); + if constexpr (CheckOnly) { + return position; + } else { + return std::make_pair( + URITemplateToken{URITemplateTokenLiteral{input.substr(0, position)}}, + position); + } } else { if (input.empty() || input[0] != '{') { return std::nullopt; @@ -297,10 +310,17 @@ auto parse_expression(const std::string_view input) -> URITemplateParseResult { var_start = 1; } - std::vector variables; - const auto end_position = parse_variable_list(input, var_start, variables); - return std::make_pair(URITemplateToken{T{std::move(variables)}}, - end_position + 1); + if constexpr (CheckOnly) { + const auto end_position = + parse_variable_list(input, var_start, nullptr); + return end_position + 1; + } else { + std::vector variables; + const auto end_position = + parse_variable_list(input, var_start, &variables); + return std::make_pair(URITemplateToken{T{std::move(variables)}}, + end_position + 1); + } } } diff --git a/src/core/uritemplate/include/sourcemeta/core/uritemplate.h b/src/core/uritemplate/include/sourcemeta/core/uritemplate.h index 4f5540ff4..b4e7fc4b9 100644 --- a/src/core/uritemplate/include/sourcemeta/core/uritemplate.h +++ b/src/core/uritemplate/include/sourcemeta/core/uritemplate.h @@ -58,6 +58,21 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplate { /// ``` URITemplate(const std::string_view source); + /// Check whether the given string is a valid RFC 6570 URI Template + /// without building the parsed representation. For example: + /// + /// ```cpp + /// #include + /// + /// #include + /// + /// assert(sourcemeta::core::URITemplate::is_uritemplate( + /// "http://example.com/~{username}/")); + /// assert(!sourcemeta::core::URITemplate::is_uritemplate("{var")); + /// ``` + [[nodiscard]] static auto is_uritemplate(std::string_view input) noexcept + -> bool; + /// Get the number of tokens in the template [[nodiscard]] auto size() const noexcept -> std::uint64_t; diff --git a/src/core/uritemplate/uritemplate.cc b/src/core/uritemplate/uritemplate.cc index 16ab67632..569b8ebf7 100644 --- a/src/core/uritemplate/uritemplate.cc +++ b/src/core/uritemplate/uritemplate.cc @@ -8,23 +8,28 @@ namespace sourcemeta::core { -template +template static auto try_parse(std::string_view &remaining, std::size_t &offset, - std::vector &tokens) -> bool { - if (auto result = parse_expression(remaining)) { - tokens.emplace_back(std::move(result->first)); - remaining.remove_prefix(result->second); - offset += result->second; + std::vector *tokens) -> bool { + if (auto result = parse_expression(remaining)) { + if constexpr (CheckOnly) { + remaining.remove_prefix(*result); + offset += *result; + } else { + tokens->emplace_back(std::move(result->first)); + remaining.remove_prefix(result->second); + offset += result->second; + } return true; } return false; } -template +template static auto try_parse_any(std::string_view &remaining, std::size_t &offset, - std::vector &tokens) -> bool { - return (try_parse(remaining, offset, tokens) || ...); + std::vector *tokens) -> bool { + return (try_parse(remaining, offset, tokens) || ...); } URITemplate::URITemplate(const std::string_view source) { @@ -33,7 +38,7 @@ URITemplate::URITemplate(const std::string_view source) { while (!remaining.empty()) { try { - if (!try_parse_any( - remaining, offset, this->tokens_)) { + remaining, offset, &this->tokens_)) { break; } } catch (URITemplateParseError &error) { @@ -50,6 +55,30 @@ URITemplate::URITemplate(const std::string_view source) { } } +auto URITemplate::is_uritemplate(const std::string_view input) noexcept + -> bool { + try { + std::string_view remaining{input}; + std::size_t offset = 0; + while (!remaining.empty()) { + if (!try_parse_any( + remaining, offset, nullptr)) { + return false; + } + } + return true; + } catch (...) { + return false; + } +} + auto URITemplate::size() const noexcept -> std::uint64_t { return static_cast(this->tokens_.size()); } diff --git a/test/uritemplate/uritemplate_parse_error_test.cc b/test/uritemplate/uritemplate_parse_error_test.cc index f0699969f..1466bd1ac 100644 --- a/test/uritemplate/uritemplate_parse_error_test.cc +++ b/test/uritemplate/uritemplate_parse_error_test.cc @@ -10,7 +10,8 @@ EXPECT_EQ(error.column(), expected_column); \ } catch (...) { \ FAIL(); \ - } + } \ + EXPECT_FALSE(sourcemeta::core::URITemplate::is_uritemplate(input)) TEST(URITemplate_parse_error, unclosed_brace) { EXPECT_URITEMPLATE_PARSE_ERROR("{var", 1); diff --git a/test/uritemplate/uritemplate_parse_test.cc b/test/uritemplate/uritemplate_parse_test.cc index 7adf9fe07..771c83203 100644 --- a/test/uritemplate/uritemplate_parse_test.cc +++ b/test/uritemplate/uritemplate_parse_test.cc @@ -6,6 +6,7 @@ TEST(URITemplate_parse, empty_string) { const sourcemeta::core::URITemplate uri_template{""}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("")); EXPECT_TRUE(uri_template.empty()); EXPECT_EQ(uri_template.size(), 0); } @@ -13,6 +14,8 @@ TEST(URITemplate_parse, empty_string) { TEST(URITemplate_parse, literal_only) { const sourcemeta::core::URITemplate uri_template{ "http://example.com/path/to/resource"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "http://example.com/path/to/resource")); EXPECT_EQ(uri_template.size(), 1); EXPECT_FALSE(uri_template.empty()); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, @@ -21,6 +24,7 @@ TEST(URITemplate_parse, literal_only) { TEST(URITemplate_parse, single_variable) { const sourcemeta::core::URITemplate uri_template{"{var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, @@ -29,6 +33,7 @@ TEST(URITemplate_parse, single_variable) { TEST(URITemplate_parse, variable_with_underscore) { const sourcemeta::core::URITemplate uri_template{"{my_var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{my_var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "my_var", 0, false); @@ -36,6 +41,7 @@ TEST(URITemplate_parse, variable_with_underscore) { TEST(URITemplate_parse, variable_with_digits) { const sourcemeta::core::URITemplate uri_template{"{var123}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{var123}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "var123", 0, false); @@ -43,6 +49,7 @@ TEST(URITemplate_parse, variable_with_digits) { TEST(URITemplate_parse, variable_with_dot) { const sourcemeta::core::URITemplate uri_template{"{foo.bar}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{foo.bar}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "foo.bar", 0, false); @@ -50,6 +57,7 @@ TEST(URITemplate_parse, variable_with_dot) { TEST(URITemplate_parse, variable_with_multiple_dots) { const sourcemeta::core::URITemplate uri_template{"{foo.bar.baz}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{foo.bar.baz}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "foo.bar.baz", 0, false); @@ -57,6 +65,7 @@ TEST(URITemplate_parse, variable_with_multiple_dots) { TEST(URITemplate_parse, variable_with_percent_encoded) { const sourcemeta::core::URITemplate uri_template{"{%3Bvar}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{%3Bvar}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "%3Bvar", 0, false); @@ -64,6 +73,8 @@ TEST(URITemplate_parse, variable_with_percent_encoded) { TEST(URITemplate_parse, literal_then_variable) { const sourcemeta::core::URITemplate uri_template{"http://example.com/{id}"}; + EXPECT_TRUE( + sourcemeta::core::URITemplate::is_uritemplate("http://example.com/{id}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "http://example.com/"); EXPECT_URITEMPLATE_EXPANSION(uri_template, 1, URITemplateTokenVariable, 0, @@ -72,6 +83,7 @@ TEST(URITemplate_parse, literal_then_variable) { TEST(URITemplate_parse, variable_then_literal) { const sourcemeta::core::URITemplate uri_template{"{id}/resource"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{id}/resource")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "id", 0, false); @@ -81,6 +93,8 @@ TEST(URITemplate_parse, variable_then_literal) { TEST(URITemplate_parse, literal_variable_literal) { const sourcemeta::core::URITemplate uri_template{ "http://example.com/~{username}/"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "http://example.com/~{username}/")); EXPECT_EQ(uri_template.size(), 3); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "http://example.com/~"); EXPECT_URITEMPLATE_EXPANSION(uri_template, 1, URITemplateTokenVariable, 0, @@ -91,6 +105,8 @@ TEST(URITemplate_parse, literal_variable_literal) { TEST(URITemplate_parse, multiple_variables) { const sourcemeta::core::URITemplate uri_template{ "/users/{id}/posts/{postId}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "/users/{id}/posts/{postId}")); EXPECT_EQ(uri_template.size(), 4); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "/users/"); EXPECT_URITEMPLATE_EXPANSION(uri_template, 1, URITemplateTokenVariable, 0, @@ -102,6 +118,7 @@ TEST(URITemplate_parse, multiple_variables) { TEST(URITemplate_parse, adjacent_variables) { const sourcemeta::core::URITemplate uri_template{"{x}{y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{x}{y}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "x", 0, false); @@ -111,6 +128,7 @@ TEST(URITemplate_parse, adjacent_variables) { TEST(URITemplate_parse, reserved_expansion) { const sourcemeta::core::URITemplate uri_template{"{+var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{+var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenReservedExpansion, 0, "var", 0, false); @@ -118,6 +136,7 @@ TEST(URITemplate_parse, reserved_expansion) { TEST(URITemplate_parse, reserved_expansion_with_dots) { const sourcemeta::core::URITemplate uri_template{"{+foo.bar}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{+foo.bar}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenReservedExpansion, 0, "foo.bar", @@ -126,6 +145,8 @@ TEST(URITemplate_parse, reserved_expansion_with_dots) { TEST(URITemplate_parse, reserved_expansion_with_literal) { const sourcemeta::core::URITemplate uri_template{"http://example.com{+path}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "http://example.com{+path}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "http://example.com"); EXPECT_URITEMPLATE_EXPANSION( @@ -134,6 +155,7 @@ TEST(URITemplate_parse, reserved_expansion_with_literal) { TEST(URITemplate_parse, fragment_expansion) { const sourcemeta::core::URITemplate uri_template{"{#var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{#var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenFragmentExpansion, 0, "var", 0, false); @@ -141,6 +163,7 @@ TEST(URITemplate_parse, fragment_expansion) { TEST(URITemplate_parse, fragment_expansion_with_dots) { const sourcemeta::core::URITemplate uri_template{"{#foo.bar}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{#foo.bar}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenFragmentExpansion, 0, "foo.bar", @@ -149,6 +172,8 @@ TEST(URITemplate_parse, fragment_expansion_with_dots) { TEST(URITemplate_parse, fragment_expansion_with_literal) { const sourcemeta::core::URITemplate uri_template{"http://example.com{#frag}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "http://example.com{#frag}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "http://example.com"); EXPECT_URITEMPLATE_EXPANSION( @@ -158,6 +183,8 @@ TEST(URITemplate_parse, fragment_expansion_with_literal) { TEST(URITemplate_parse, mixed_level1_and_level2) { const sourcemeta::core::URITemplate uri_template{ "http://example.com{+path}{#frag}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "http://example.com{+path}{#frag}")); EXPECT_EQ(uri_template.size(), 3); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "http://example.com"); EXPECT_URITEMPLATE_EXPANSION( @@ -168,6 +195,7 @@ TEST(URITemplate_parse, mixed_level1_and_level2) { TEST(URITemplate_parse, variable_list_two) { const sourcemeta::core::URITemplate uri_template{"{x,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{x,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "x", 0, false); @@ -177,6 +205,7 @@ TEST(URITemplate_parse, variable_list_two) { TEST(URITemplate_parse, variable_list_three) { const sourcemeta::core::URITemplate uri_template{"{x,hello,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{x,hello,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "x", 0, false); @@ -188,6 +217,7 @@ TEST(URITemplate_parse, variable_list_three) { TEST(URITemplate_parse, variable_list_with_literal) { const sourcemeta::core::URITemplate uri_template{"map?{x,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("map?{x,y}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "map?"); EXPECT_URITEMPLATE_EXPANSION(uri_template, 1, URITemplateTokenVariable, 0, @@ -198,6 +228,7 @@ TEST(URITemplate_parse, variable_list_with_literal) { TEST(URITemplate_parse, reserved_expansion_variable_list) { const sourcemeta::core::URITemplate uri_template{"{+x,hello,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{+x,hello,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenReservedExpansion, 0, "x", 0, false); @@ -209,6 +240,7 @@ TEST(URITemplate_parse, reserved_expansion_variable_list) { TEST(URITemplate_parse, reserved_expansion_variable_list_with_literal) { const sourcemeta::core::URITemplate uri_template{"{+path,x}/here"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{+path,x}/here")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenReservedExpansion, 0, "path", 0, false); @@ -219,6 +251,7 @@ TEST(URITemplate_parse, reserved_expansion_variable_list_with_literal) { TEST(URITemplate_parse, fragment_expansion_variable_list) { const sourcemeta::core::URITemplate uri_template{"{#x,hello,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{#x,hello,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenFragmentExpansion, 0, "x", 0, false); @@ -230,6 +263,7 @@ TEST(URITemplate_parse, fragment_expansion_variable_list) { TEST(URITemplate_parse, fragment_expansion_variable_list_with_literal) { const sourcemeta::core::URITemplate uri_template{"{#path,x}/here"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{#path,x}/here")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenFragmentExpansion, 0, "path", 0, false); @@ -240,6 +274,7 @@ TEST(URITemplate_parse, fragment_expansion_variable_list_with_literal) { TEST(URITemplate_parse, label_expansion_single) { const sourcemeta::core::URITemplate uri_template{"{.var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{.var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenLabelExpansion, 0, "var", 0, false); @@ -247,6 +282,7 @@ TEST(URITemplate_parse, label_expansion_single) { TEST(URITemplate_parse, label_expansion_multiple) { const sourcemeta::core::URITemplate uri_template{"{.x,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{.x,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenLabelExpansion, 0, "x", 0, false); @@ -256,6 +292,7 @@ TEST(URITemplate_parse, label_expansion_multiple) { TEST(URITemplate_parse, label_expansion_with_literal) { const sourcemeta::core::URITemplate uri_template{"X{.var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("X{.var}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "X"); EXPECT_URITEMPLATE_EXPANSION(uri_template, 1, URITemplateTokenLabelExpansion, @@ -264,6 +301,7 @@ TEST(URITemplate_parse, label_expansion_with_literal) { TEST(URITemplate_parse, path_expansion_single) { const sourcemeta::core::URITemplate uri_template{"{/var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{/var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathExpansion, 0, "var", 0, false); @@ -271,6 +309,7 @@ TEST(URITemplate_parse, path_expansion_single) { TEST(URITemplate_parse, path_expansion_multiple) { const sourcemeta::core::URITemplate uri_template{"{/var,x}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{/var,x}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathExpansion, 0, "var", 0, false); @@ -280,6 +319,7 @@ TEST(URITemplate_parse, path_expansion_multiple) { TEST(URITemplate_parse, path_expansion_with_literal) { const sourcemeta::core::URITemplate uri_template{"{/var,x}/here"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{/var,x}/here")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathExpansion, 0, "var", 0, false); @@ -290,6 +330,7 @@ TEST(URITemplate_parse, path_expansion_with_literal) { TEST(URITemplate_parse, path_parameter_expansion_single) { const sourcemeta::core::URITemplate uri_template{"{;x}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{;x}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathParameterExpansion, 0, "x", @@ -298,6 +339,7 @@ TEST(URITemplate_parse, path_parameter_expansion_single) { TEST(URITemplate_parse, path_parameter_expansion_multiple) { const sourcemeta::core::URITemplate uri_template{"{;x,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{;x,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathParameterExpansion, 0, "x", @@ -309,6 +351,7 @@ TEST(URITemplate_parse, path_parameter_expansion_multiple) { TEST(URITemplate_parse, path_parameter_expansion_three) { const sourcemeta::core::URITemplate uri_template{"{;x,y,empty}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{;x,y,empty}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathParameterExpansion, 0, "x", @@ -323,6 +366,7 @@ TEST(URITemplate_parse, path_parameter_expansion_three) { TEST(URITemplate_parse, query_expansion_single) { const sourcemeta::core::URITemplate uri_template{"{?x}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{?x}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryExpansion, 0, "x", 0, false); @@ -330,6 +374,7 @@ TEST(URITemplate_parse, query_expansion_single) { TEST(URITemplate_parse, query_expansion_multiple) { const sourcemeta::core::URITemplate uri_template{"{?x,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{?x,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryExpansion, 0, "x", 0, false); @@ -339,6 +384,7 @@ TEST(URITemplate_parse, query_expansion_multiple) { TEST(URITemplate_parse, query_expansion_three) { const sourcemeta::core::URITemplate uri_template{"{?x,y,empty}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{?x,y,empty}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryExpansion, 0, "x", 0, false); @@ -350,6 +396,7 @@ TEST(URITemplate_parse, query_expansion_three) { TEST(URITemplate_parse, query_continuation_expansion_single) { const sourcemeta::core::URITemplate uri_template{"{&x}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{&x}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryContinuationExpansion, 0, @@ -358,6 +405,7 @@ TEST(URITemplate_parse, query_continuation_expansion_single) { TEST(URITemplate_parse, query_continuation_expansion_multiple) { const sourcemeta::core::URITemplate uri_template{"{&x,y,empty}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{&x,y,empty}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryContinuationExpansion, 0, @@ -372,6 +420,7 @@ TEST(URITemplate_parse, query_continuation_expansion_multiple) { TEST(URITemplate_parse, query_continuation_with_literal) { const sourcemeta::core::URITemplate uri_template{"?fixed=yes{&x}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("?fixed=yes{&x}")); EXPECT_EQ(uri_template.size(), 2); EXPECT_URITEMPLATE_LITERAL(uri_template, 0, "?fixed=yes"); EXPECT_URITEMPLATE_EXPANSION(uri_template, 1, @@ -381,6 +430,7 @@ TEST(URITemplate_parse, query_continuation_with_literal) { TEST(URITemplate_parse, prefix_modifier) { const sourcemeta::core::URITemplate uri_template{"{var:3}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{var:3}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "var", 3, false); @@ -388,6 +438,7 @@ TEST(URITemplate_parse, prefix_modifier) { TEST(URITemplate_parse, prefix_modifier_max) { const sourcemeta::core::URITemplate uri_template{"{var:9999}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{var:9999}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "var", 9999, false); @@ -395,6 +446,7 @@ TEST(URITemplate_parse, prefix_modifier_max) { TEST(URITemplate_parse, explode_modifier) { const sourcemeta::core::URITemplate uri_template{"{list*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{list*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "list", 0, true); @@ -402,6 +454,7 @@ TEST(URITemplate_parse, explode_modifier) { TEST(URITemplate_parse, reserved_with_prefix) { const sourcemeta::core::URITemplate uri_template{"{+path:6}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{+path:6}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenReservedExpansion, 0, "path", 6, false); @@ -409,6 +462,7 @@ TEST(URITemplate_parse, reserved_with_prefix) { TEST(URITemplate_parse, fragment_with_prefix) { const sourcemeta::core::URITemplate uri_template{"{#path:6}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{#path:6}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenFragmentExpansion, 0, "path", 6, false); @@ -416,6 +470,7 @@ TEST(URITemplate_parse, fragment_with_prefix) { TEST(URITemplate_parse, label_with_prefix) { const sourcemeta::core::URITemplate uri_template{"{.var:3}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{.var:3}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenLabelExpansion, 0, "var", 3, false); @@ -423,6 +478,7 @@ TEST(URITemplate_parse, label_with_prefix) { TEST(URITemplate_parse, path_with_explode) { const sourcemeta::core::URITemplate uri_template{"{/list*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{/list*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathExpansion, 0, "list", 0, true); @@ -430,6 +486,7 @@ TEST(URITemplate_parse, path_with_explode) { TEST(URITemplate_parse, query_with_prefix) { const sourcemeta::core::URITemplate uri_template{"{?var:3}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{?var:3}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryExpansion, 0, "var", 3, false); @@ -437,6 +494,7 @@ TEST(URITemplate_parse, query_with_prefix) { TEST(URITemplate_parse, mixed_modifiers_in_list) { const sourcemeta::core::URITemplate uri_template{"{/var:1,var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{/var:1,var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathExpansion, 0, "var", 1, false); @@ -446,6 +504,7 @@ TEST(URITemplate_parse, mixed_modifiers_in_list) { TEST(URITemplate_parse, variable_single_char) { const sourcemeta::core::URITemplate uri_template{"{x}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{x}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "x", 0, false); @@ -453,6 +512,7 @@ TEST(URITemplate_parse, variable_single_char) { TEST(URITemplate_parse, variable_starting_with_digit) { const sourcemeta::core::URITemplate uri_template{"{1var}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{1var}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "1var", 0, false); @@ -460,6 +520,7 @@ TEST(URITemplate_parse, variable_starting_with_digit) { TEST(URITemplate_parse, variable_only_digit) { const sourcemeta::core::URITemplate uri_template{"{1}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{1}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "1", 0, false); @@ -467,6 +528,7 @@ TEST(URITemplate_parse, variable_only_digit) { TEST(URITemplate_parse, variable_only_underscore) { const sourcemeta::core::URITemplate uri_template{"{_}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{_}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "_", 0, false); @@ -474,6 +536,7 @@ TEST(URITemplate_parse, variable_only_underscore) { TEST(URITemplate_parse, variable_starting_with_underscore) { const sourcemeta::core::URITemplate uri_template{"{_private}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{_private}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "_private", 0, false); @@ -481,6 +544,7 @@ TEST(URITemplate_parse, variable_starting_with_underscore) { TEST(URITemplate_parse, variable_percent_encoded_start) { const sourcemeta::core::URITemplate uri_template{"{%41bc}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{%41bc}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "%41bc", 0, false); @@ -488,6 +552,7 @@ TEST(URITemplate_parse, variable_percent_encoded_start) { TEST(URITemplate_parse, variable_multiple_percent_encoded) { const sourcemeta::core::URITemplate uri_template{"{%41%42%43}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{%41%42%43}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "%41%42%43", 0, false); @@ -495,6 +560,7 @@ TEST(URITemplate_parse, variable_multiple_percent_encoded) { TEST(URITemplate_parse, variable_with_dot_and_digits) { const sourcemeta::core::URITemplate uri_template{"{a.1.b.2}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{a.1.b.2}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "a.1.b.2", 0, false); @@ -502,6 +568,7 @@ TEST(URITemplate_parse, variable_with_dot_and_digits) { TEST(URITemplate_parse, prefix_single_digit) { const sourcemeta::core::URITemplate uri_template{"{var:1}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{var:1}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "var", 1, false); @@ -509,6 +576,7 @@ TEST(URITemplate_parse, prefix_single_digit) { TEST(URITemplate_parse, prefix_four_digits) { const sourcemeta::core::URITemplate uri_template{"{var:1234}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{var:1234}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "var", 1234, false); @@ -516,6 +584,7 @@ TEST(URITemplate_parse, prefix_four_digits) { TEST(URITemplate_parse, explode_with_reserved_expansion) { const sourcemeta::core::URITemplate uri_template{"{+list*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{+list*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenReservedExpansion, 0, "list", 0, true); @@ -523,6 +592,7 @@ TEST(URITemplate_parse, explode_with_reserved_expansion) { TEST(URITemplate_parse, explode_with_fragment_expansion) { const sourcemeta::core::URITemplate uri_template{"{#keys*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{#keys*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenFragmentExpansion, 0, "keys", 0, true); @@ -530,6 +600,7 @@ TEST(URITemplate_parse, explode_with_fragment_expansion) { TEST(URITemplate_parse, explode_with_label_expansion) { const sourcemeta::core::URITemplate uri_template{"{.list*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{.list*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenLabelExpansion, 0, "list", 0, true); @@ -537,6 +608,7 @@ TEST(URITemplate_parse, explode_with_label_expansion) { TEST(URITemplate_parse, explode_with_path_parameter) { const sourcemeta::core::URITemplate uri_template{"{;keys*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{;keys*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenPathParameterExpansion, 0, @@ -545,6 +617,7 @@ TEST(URITemplate_parse, explode_with_path_parameter) { TEST(URITemplate_parse, explode_with_query_expansion) { const sourcemeta::core::URITemplate uri_template{"{?list*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{?list*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryExpansion, 0, "list", 0, true); @@ -552,6 +625,7 @@ TEST(URITemplate_parse, explode_with_query_expansion) { TEST(URITemplate_parse, explode_with_query_continuation) { const sourcemeta::core::URITemplate uri_template{"{&keys*}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{&keys*}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryContinuationExpansion, 0, @@ -560,6 +634,7 @@ TEST(URITemplate_parse, explode_with_query_continuation) { TEST(URITemplate_parse, multiple_variables_with_explode) { const sourcemeta::core::URITemplate uri_template{"{?x,list*,y}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{?x,list*,y}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenQueryExpansion, 0, "x", 0, false); @@ -571,6 +646,7 @@ TEST(URITemplate_parse, multiple_variables_with_explode) { TEST(URITemplate_parse, multiple_variables_with_prefix) { const sourcemeta::core::URITemplate uri_template{"{x:3,y:5,z}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{x:3,y:5,z}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "x", 3, false); @@ -583,6 +659,8 @@ TEST(URITemplate_parse, multiple_variables_with_prefix) { TEST(URITemplate_parse, long_variable_name) { const sourcemeta::core::URITemplate uri_template{ "{abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate( + "{abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION( uri_template, 0, URITemplateTokenVariable, 0, @@ -592,6 +670,8 @@ TEST(URITemplate_parse, long_variable_name) { TEST(URITemplate_parse, variable_list_many) { const sourcemeta::core::URITemplate uri_template{"{a,b,c,d,e,f,g,h,i,j}"}; + EXPECT_TRUE( + sourcemeta::core::URITemplate::is_uritemplate("{a,b,c,d,e,f,g,h,i,j}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "a", 0, false); @@ -617,6 +697,7 @@ TEST(URITemplate_parse, variable_list_many) { TEST(URITemplate_parse, lowercase_percent_encoding) { const sourcemeta::core::URITemplate uri_template{"{%3avar}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{%3avar}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "%3avar", 0, false); @@ -624,6 +705,7 @@ TEST(URITemplate_parse, lowercase_percent_encoding) { TEST(URITemplate_parse, mixed_case_percent_encoding) { const sourcemeta::core::URITemplate uri_template{"{%3Avar}"}; + EXPECT_TRUE(sourcemeta::core::URITemplate::is_uritemplate("{%3Avar}")); EXPECT_EQ(uri_template.size(), 1); EXPECT_URITEMPLATE_EXPANSION(uri_template, 0, URITemplateTokenVariable, 0, "%3Avar", 0, false);