Commit 0e5c63e8 authored by Ben Kelly's avatar Ben Kelly Committed by Commit Bot

URLPattern: Translate path-to-regexp parser to c++.

This CL translates the path-to-regexp parser from typescript to c++.
Its based on the code here:

https://github.com/pillarjs/path-to-regexp/blob/125c43e6481f68cc771a5af22b914acdb8c5ba1f/src/index.ts#L126-L232

We deviate from the path-to-regexp code in some terminology and output
structure.  In particular, we output a list of Part structures with
types of kFixed, kRegex, kSegmentWildcard, or kFullWildcard as discussed
in the design doc:

https://docs.google.com/document/d/17L6b3zlTHtyxQvOAvbK55gQOi5rrJLERwjt_sKXpzqc/edit#heading=h.ymw6rim68920

Note, this CL does deviate from the design doc as well in that we are
translating a more modern version of path-to-regexp than originally
planned.  This uses a "{...}?" style grouping for applying modifiers.
We also do not yet fully include support for the "*" wildcard character.
As such the grammar in the design doc does not really apply fully to
this CL.

Bug: 1141510
Change-Id: I1c3e4ef76587496301d2171fa9f76b51754dce0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518206
Commit-Queue: Ben Kelly <wanderview@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826968}
parent 2c7f8a5d
...@@ -15,9 +15,12 @@ component("liburlpattern") { ...@@ -15,9 +15,12 @@ component("liburlpattern") {
sources = [ sources = [
"parse.cc", "parse.cc",
"parse.h", "parse.h",
"pattern.cc",
"pattern.h", "pattern.h",
"tokenize.cc", "tokenize.cc",
"tokenize.h", "tokenize.h",
"utils.cc",
"utils.h",
] ]
} }
...@@ -30,8 +33,9 @@ test("liburlpattern_unittests") { ...@@ -30,8 +33,9 @@ test("liburlpattern_unittests") {
# Note, also update the local modifications in README.chromium. # Note, also update the local modifications in README.chromium.
sources = [ sources = [
"liburlpattern_unittest.cc", "parse_unittest.cc",
"tokenize_unittest.cc", "tokenize_unittest.cc",
"utils_unittest.cc",
] ]
testonly = true testonly = true
} }
...@@ -19,6 +19,12 @@ Local Modifications: ...@@ -19,6 +19,12 @@ Local Modifications:
third_party/liburlpattern/BUILD.gn third_party/liburlpattern/BUILD.gn
third_party/liburlpattern/parse.h third_party/liburlpattern/parse.h
third_party/liburlpattern/parse.cc third_party/liburlpattern/parse.cc
third_party/liburlpattern/parse_unittest.cc
third_party/liburlpattern/pattern.h
third_party/liburlpattern/pattern.cc
third_party/liburlpattern/tokenize.h third_party/liburlpattern/tokenize.h
third_party/liburlpattern/tokenize.cc third_party/liburlpattern/tokenize.cc
third_party/liburlpattern/tokenize_unittest.cc third_party/liburlpattern/tokenize_unittest.cc
third_party/liburlpattern/utils.h
third_party/liburlpattern/utils.cc
third_party/liburlpattern/utils_unittest.cc
This diff is collapsed.
...@@ -18,8 +18,16 @@ class Pattern; ...@@ -18,8 +18,16 @@ class Pattern;
// Parse a pattern string and return the result. The input |pattern| must // Parse a pattern string and return the result. The input |pattern| must
// consist of ASCII characters. Any non-ASCII characters should be UTF-8 // consist of ASCII characters. Any non-ASCII characters should be UTF-8
// encoded and % escaped, similar to URLs, prior to calling this function. // encoded and % escaped, similar to URLs, prior to calling this function.
// |delimiter_list| contains a list of characters that are considered segment
// separators when performing a kSegmentWildcard. This is the behavior you
// get when you specify a name `:foo` without a custom regular expression.
// The |prefix_list| contains a list of characters to automatically treat
// as a prefix when they appear before a kName or kRegex Token; e.g. "/:foo",
// includes the leading "/" as the prefix for the "foo" named group by default.
COMPONENT_EXPORT(LIBURLPATTERN) COMPONENT_EXPORT(LIBURLPATTERN)
absl::StatusOr<Pattern> Parse(absl::string_view pattern); absl::StatusOr<Pattern> Parse(absl::string_view pattern,
absl::string_view delimiter_list = "/#?",
absl::string_view prefix_list = "./");
} // namespace liburlpattern } // namespace liburlpattern
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.
#include "third_party/liburlpattern/parse.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/liburlpattern/pattern.h"
namespace liburlpattern {
void RunParseTest(absl::string_view pattern,
absl::StatusOr<std::vector<Part>> expected) {
auto result = Parse(pattern);
ASSERT_EQ(result.ok(), expected.ok())
<< "parse status '" << result.status() << "' for: " << pattern;
if (!expected.ok()) {
ASSERT_EQ(result.status().code(), expected.status().code())
<< "parse status code for: " << pattern;
EXPECT_NE(result.status().message().find(expected.status().message()),
std::string::npos)
<< "parse message '" << result.status().message()
<< "' does not contain '" << expected.status().message()
<< "' for: " << pattern;
return;
}
const auto& expected_part_list = expected.value();
const auto& part_list = result.value().PartList();
EXPECT_EQ(part_list.size(), expected_part_list.size())
<< "parser should produce expected number of parts for: " << pattern;
for (size_t i = 0; i < part_list.size() && i < expected_part_list.size();
++i) {
EXPECT_EQ(part_list[i], expected_part_list[i])
<< "token at index " << i << " wrong for: " << pattern;
}
}
TEST(ParseTest, EmptyPattern) {
RunParseTest("", std::vector<Part>());
}
TEST(ParseTest, InvalidChar) {
RunParseTest("/foo/ßar", absl::InvalidArgumentError("Invalid character"));
}
TEST(ParseTest, Fixed) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("/foo", expected_parts);
}
TEST(ParseTest, FixedInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
};
RunParseTest("{/foo}", expected_parts);
}
TEST(ParseTest, FixedAndEmptyGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/f", Modifier::kNone),
Part(PartType::kFixed, "oo", Modifier::kNone),
};
RunParseTest("/f{}oo", expected_parts);
}
TEST(ParseTest, FixedInGroupWithOptionalModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kOptional),
};
RunParseTest("{/foo}?", expected_parts);
}
TEST(ParseTest, FixedInGroupWithZeroOrMoreModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kZeroOrMore),
};
RunParseTest("{/foo}*", expected_parts);
}
TEST(ParseTest, FixedInGroupWithOneOrMoreModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kOneOrMore),
};
RunParseTest("{/foo}+", expected_parts);
}
TEST(ParseTest, FixedInEarlyTerminatedGroup) {
RunParseTest("{/foo", absl::InvalidArgumentError("expected CLOSE"));
}
TEST(ParseTest, FixedInUnbalancedGroup) {
RunParseTest("{/foo?", absl::InvalidArgumentError("expected CLOSE"));
}
TEST(ParseTest, FixedWithModifier) {
RunParseTest("/foo?", absl::InvalidArgumentError("Unexpected MODIFIER"));
}
TEST(ParseTest, Regex) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/f", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
Modifier::kNone),
};
RunParseTest("/f(oo)", expected_parts);
}
TEST(ParseTest, RegexInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/f", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"", "oo", /*suffix=*/"",
Modifier::kNone),
};
RunParseTest("/f{(oo)}", expected_parts);
}
TEST(ParseTest, RegexWithPrefixAndSuffixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"f", "o", /*suffix=*/"o",
Modifier::kNone),
};
RunParseTest("/{f(o)o}", expected_parts);
}
TEST(ParseTest, RegexAndRegexInGroup) {
RunParseTest("/f{(o)(o)}", absl::InvalidArgumentError("expected CLOSE"));
}
TEST(ParseTest, RegexWithPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo", /*suffix=*/"",
Modifier::kNone),
};
RunParseTest("/(foo)", expected_parts);
}
TEST(ParseTest, RegexWithNameAndPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"/", "[^/]+?",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo/:bar([^/]+?)", expected_parts);
}
TEST(ParseTest, RegexWithNameAndPrefixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo/", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo/{:bar([^/]+?)}", expected_parts);
}
TEST(ParseTest, RegexWithModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kRegex, /*name=*/"0", /*prefix=*/"/", "foo",
/*suffix=*/"", Modifier::kOptional),
};
RunParseTest("/(foo)?", expected_parts);
}
TEST(ParseTest, RegexLikeFullWildcard) {
std::vector<Part> expected_parts = {
Part(PartType::kFullWildcard, /*name=*/"0", /*prefix=*/"/", "",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/(.*)", expected_parts);
}
TEST(ParseTest, Name) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo:bar", expected_parts);
}
TEST(ParseTest, NameInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo{:bar}", expected_parts);
}
TEST(ParseTest, NameAndNameInGroup) {
RunParseTest("/foo{:bar:baz}", absl::InvalidArgumentError("expected CLOSE"));
}
TEST(ParseTest, NameWithPrefixAndSuffixInGroup) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo/", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"data_",
/*value=*/"", /*suffix=*/".jpg", Modifier::kNone),
};
RunParseTest("/foo/{data_:bar.jpg}", expected_parts);
}
TEST(ParseTest, NameWithPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"/",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo/:bar", expected_parts);
}
TEST(ParseTest, NameWithEscapedPrefix) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo/", Modifier::kNone),
Part(PartType::kSegmentWildcard, /*name=*/"bar", /*prefix=*/"",
/*value=*/"", /*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo\\/:bar", expected_parts);
}
TEST(ParseTest, NameWithCustomRegex) {
std::vector<Part> expected_parts = {
Part(PartType::kFixed, "/foo", Modifier::kNone),
Part(PartType::kRegex, /*name=*/"bar", /*prefix=*/"", "[^/]+?",
/*suffix=*/"", Modifier::kNone),
};
RunParseTest("/foo:bar([^/]+?)", expected_parts);
}
TEST(ParseTest, NameWithModifier) {
std::vector<Part> expected_parts = {
Part(PartType::kSegmentWildcard, /*name=*/"foo", /*prefix=*/"/",
/*value=*/"", /*suffix=*/"", Modifier::kOptional),
};
RunParseTest("/:foo?", expected_parts);
}
} // namespace liburlpattern
// Copyright 2020 The Chromium Authors. All rights reserved.
// Copyright 2014 Blake Embrey (hello@blakeembrey.com)
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.
#include "third_party/liburlpattern/pattern.h"
#include "third_party/abseil-cpp/absl/base/macros.h"
#include "third_party/abseil-cpp/absl/strings/str_format.h"
namespace liburlpattern {
std::ostream& operator<<(std::ostream& o, Part part) {
o << "{ type:" << static_cast<int>(part.type) << ", name:" << part.name
<< ", prefix:" << part.prefix << ", value:" << part.value
<< ", suffix:" << part.suffix
<< ", modifier:" << static_cast<int>(part.modifier) << " }";
return o;
}
Part::Part(PartType t, std::string v, Modifier m)
: type(t), value(std::move(v)), modifier(m) {
ABSL_ASSERT(type == PartType::kFixed);
}
Part::Part(PartType t,
std::string n,
std::string p,
std::string v,
std::string s,
Modifier m)
: type(t),
name(std::move(n)),
prefix(std::move(p)),
value(std::move(v)),
suffix(std::move(s)),
modifier(m) {
ABSL_ASSERT(type != PartType::kFixed);
ABSL_ASSERT(!name.empty());
if (type == PartType::kFullWildcard || type == PartType::kSegmentWildcard)
ABSL_ASSERT(value.empty());
}
Pattern::Pattern(std::vector<Part> part_list)
: part_list_(std::move(part_list)) {}
} // namespace liburlpattern
// Copyright 2020 The Chromium Authors. All rights reserved. // Copyright 2020 The Chromium Authors. All rights reserved.
// Copyright 2014 Blake Embrey (hello@blakeembrey.com)
// Use of this source code is governed by an MIT-style license that can be // Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT. // found in the LICENSE file or at https://opensource.org/licenses/MIT.
#ifndef THIRD_PARTY_LIBURLPATTERN_PATTERN_H_ #ifndef THIRD_PARTY_LIBURLPATTERN_PATTERN_H_
#define THIRD_PARTY_LIBURLPATTERN_PATTERN_H_ #define THIRD_PARTY_LIBURLPATTERN_PATTERN_H_
#include <string>
#include <vector>
#include "base/component_export.h" #include "base/component_export.h"
// NOTE: This code is a work-in-progress. It is not ready for production use. // NOTE: This code is a work-in-progress. It is not ready for production use.
namespace liburlpattern { namespace liburlpattern {
enum class PartType {
// A fixed, non-variable part of the pattern. Consists of kChar and
// kEscapedChar Tokens.
kFixed,
// A part with a custom regular expression.
kRegex,
// A part that matches any character to the next segment separator.
kSegmentWildcard,
// A part that matches any character to the end of the input string.
kFullWildcard,
};
enum class Modifier {
// No modifier.
kNone,
// The `?` modifier.
kOptional,
// The `*` modifier.
kZeroOrMore,
// The `+` modifier.
kOneOrMore,
};
// A structure representing one part of a parsed Pattern. A full Pattern
// consists of an ordered sequence of Part objects.
struct COMPONENT_EXPORT(LIBURLPATTERN) Part {
// The type of the Part.
const PartType type = PartType::kFixed;
// The name of the Part. Only kRegex, kSegmentWildcard, and kFullWildcard
// parts may have a |name|. kFixed parts must have an empty |name|.
const std::string name;
// A fixed string prefix that is expected before any regex or wildcard match.
// kFixed parts must have an empty |prefix|.
const std::string prefix;
// The meaning of the |value| depends on the |type| of the Part. For kFixed
// parts the |value| contains the fixed string to match. For kRegex parts
// the |value| contains a regular expression to match. The |value| is empty
// for kSegmentWildcard and kFullWildcard parts since the |type| encodes what
// to match.
const std::string value;
// A fixed string prefix that is expected after any regex or wildcard match.
// kFixed parts must have an empty |suffix|.
const std::string suffix;
// A |modifier| indicating whether the Part is optional and/or repeated. Any
// Part type may have a |modifier|.
const Modifier modifier = Modifier::kNone;
Part(PartType type, std::string value, Modifier modifier);
Part(PartType type,
std::string name,
std::string prefix,
std::string value,
std::string suffix,
Modifier modifier);
Part() = default;
};
COMPONENT_EXPORT(LIBURLPATTERN)
inline bool operator==(const Part& lh, const Part& rh) {
return lh.name == rh.name && lh.prefix == rh.prefix && lh.value == rh.value &&
lh.suffix == rh.suffix && lh.modifier == rh.modifier;
}
inline bool operator!=(const Part& lh, const Part& rh) {
return !(lh == rh);
}
COMPONENT_EXPORT(LIBURLPATTERN)
std::ostream& operator<<(std::ostream& o, Part part);
// This class represents a successfully parsed pattern string. It will contain // This class represents a successfully parsed pattern string. It will contain
// an intermediate representation that can be used to generate either a regular // an intermediate representation that can be used to generate either a regular
// expression string or to directly match against input strings. Not all // expression string or to directly match against input strings. Not all
// patterns are supported for direct matching. // patterns are supported for direct matching.
class COMPONENT_EXPORT(LIBURLPATTERN) Pattern { class COMPONENT_EXPORT(LIBURLPATTERN) Pattern {
// TODO: Implement pattern details. public:
explicit Pattern(std::vector<Part> part_list);
const std::vector<Part>& PartList() const { return part_list_; }
private:
std::vector<Part> part_list_;
}; };
} // namespace liburlpattern } // namespace liburlpattern
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
#include "third_party/abseil-cpp/absl/strings/str_format.h" #include "third_party/abseil-cpp/absl/strings/str_format.h"
// The following code is a translation from the path-to-regexp typescript at:
//
// https://github.com/pillarjs/path-to-regexp/blob/125c43e6481f68cc771a5af22b914acdb8c5ba1f/src/index.ts#L4-L124
namespace liburlpattern { namespace liburlpattern {
namespace { namespace {
...@@ -24,16 +28,34 @@ bool IsNameChar(char c) { ...@@ -24,16 +28,34 @@ bool IsNameChar(char c) {
} // namespace } // namespace
const char* TokenTypeToString(TokenType type) {
switch (type) {
case TokenType::kOpen:
return "OPEN";
case TokenType::kClose:
return "CLOSE";
case TokenType::kRegex:
return "REGEX";
case TokenType::kName:
return "NAME";
case TokenType::kChar:
return "CHAR";
case TokenType::kEscapedChar:
return "ESCAPED_CHAR";
case TokenType::kModifier:
return "MODIFIER";
case TokenType::kEnd:
return "END";
}
}
std::ostream& operator<<(std::ostream& o, Token token) { std::ostream& operator<<(std::ostream& o, Token token) {
o << "{ type:" << static_cast<int>(token.type) << ", index:" << token.index o << "{ type:" << static_cast<int>(token.type) << ", index:" << token.index
<< ", value:" << token.value << " }"; << ", value:" << token.value << " }";
return o; return o;
} }
// Split the input pattern into a list of tokens. Originally translated to // Split the input pattern into a list of tokens.
// c++ from:
//
// https://github.com/pillarjs/path-to-regexp/blob/125c43e6481f68cc771a5af22b914acdb8c5ba1f/src/index.ts#L4-L124
absl::StatusOr<std::vector<Token>> Tokenize(absl::string_view pattern) { absl::StatusOr<std::vector<Token>> Tokenize(absl::string_view pattern) {
// Verify that all characters are valid before parsing. This simplifies the // Verify that all characters are valid before parsing. This simplifies the
// following logic. // following logic.
......
...@@ -39,17 +39,19 @@ enum class TokenType { ...@@ -39,17 +39,19 @@ enum class TokenType {
kEnd, kEnd,
}; };
const char* TokenTypeToString(TokenType type);
// Simple structure representing a single lexical token. // Simple structure representing a single lexical token.
struct COMPONENT_EXPORT(LIBURLPATTERN) Token { struct COMPONENT_EXPORT(LIBURLPATTERN) Token {
// Indicate the token type. // Indicate the token type.
TokenType type = TokenType::kEnd; const TokenType type = TokenType::kEnd;
// Index of the start of this token in the original pattern string. // Index of the start of this token in the original pattern string.
size_t index = 0; const size_t index = 0;
// The value of the token. May be one or many characters depending on type. // The value of the token. May be one or many characters depending on type.
// May be null zero characters for the kEnd type. // May be null zero characters for the kEnd type.
absl::string_view value; const absl::string_view value;
Token(TokenType t, size_t i, absl::string_view v) Token(TokenType t, size_t i, absl::string_view v)
: type(t), index(i), value(v) {} : type(t), index(i), value(v) {}
...@@ -61,7 +63,6 @@ inline bool operator==(const Token& lh, const Token& rh) { ...@@ -61,7 +63,6 @@ inline bool operator==(const Token& lh, const Token& rh) {
return lh.type == rh.type && lh.index == rh.index && lh.value == rh.value; return lh.type == rh.type && lh.index == rh.index && lh.value == rh.value;
} }
COMPONENT_EXPORT(LIBURLPATTERN)
inline bool operator!=(const Token& lh, const Token& rh) { inline bool operator!=(const Token& lh, const Token& rh) {
return !(lh == rh); return !(lh == rh);
} }
......
// Copyright 2020 The Chromium Authors. All rights reserved. // Copyright 2020 The Chromium Authors. All rights reserved.
// Copyright 2014 Blake Embrey (hello@blakeembrey.com)
// Use of this source code is governed by an MIT-style license that can be // Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT. // found in the LICENSE file or at https://opensource.org/licenses/MIT.
#include "testing/gtest/include/gtest/gtest.h" #include "third_party/liburlpattern/utils.h"
#include "third_party/liburlpattern/parse.h"
#include "third_party/liburlpattern/pattern.h"
namespace liburlpattern { namespace liburlpattern {
TEST(ParseTest, ValidChar) { std::string EscapeString(absl::string_view input) {
auto result = Parse("/foo/bar"); std::string result;
EXPECT_TRUE(result.ok()); result.reserve(input.size());
} const absl::string_view special_characters(".+*?=^!:${}()[]|/\\");
for (auto& c : input) {
TEST(ParseTest, InvalidChar) { if (special_characters.find(c) != std::string::npos)
auto result = Parse("/foo/ßar"); result += '\\';
EXPECT_FALSE(result.ok()); result += c;
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument); }
EXPECT_NE(result.status().message().find("Invalid character"), return result;
std::string::npos);
} }
} // namespace liburlpattern } // namespace liburlpattern
// Copyright 2020 The Chromium Authors. All rights reserved.
// Copyright 2014 Blake Embrey (hello@blakeembrey.com)
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.
#ifndef THIRD_PARTY_LIBURLPATTERN_UTILS_H_
#define THIRD_PARTY_LIBURLPATTERN_UTILS_H_
#include <string>
#include "base/component_export.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
namespace liburlpattern {
// Escape an input string so that it may be safely included in a
// regular expression.
COMPONENT_EXPORT(LIBURLPATTERN)
std::string EscapeString(absl::string_view input);
} // namespace liburlpattern
#endif // THIRD_PARTY_LIBURLPATTERN_UTILS_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file or at https://opensource.org/licenses/MIT.
#include "third_party/liburlpattern/utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace liburlpattern {
TEST(UtilsTest, EscapeStringDot) {
EXPECT_EQ(EscapeString("index.html"), "index\\.html");
}
TEST(UtilsTest, EscapeStringPlus) {
EXPECT_EQ(EscapeString("foo+"), "foo\\+");
}
TEST(UtilsTest, EscapeStringStar) {
EXPECT_EQ(EscapeString("foo*"), "foo\\*");
}
TEST(UtilsTest, EscapeStringQuestion) {
EXPECT_EQ(EscapeString("foo?"), "foo\\?");
}
TEST(UtilsTest, EscapeStringEquals) {
EXPECT_EQ(EscapeString("foo=bar"), "foo\\=bar");
}
TEST(UtilsTest, EscapeStringCaret) {
EXPECT_EQ(EscapeString("^foo"), "\\^foo");
}
TEST(UtilsTest, EscapeStringBang) {
EXPECT_EQ(EscapeString("!foo"), "\\!foo");
}
TEST(UtilsTest, EscapeStringColon) {
EXPECT_EQ(EscapeString(":foo"), "\\:foo");
}
TEST(UtilsTest, EscapeStringDollar) {
EXPECT_EQ(EscapeString("foo$"), "foo\\$");
}
TEST(UtilsTest, EscapeStringCurlyBraces) {
EXPECT_EQ(EscapeString("{foo}"), "\\{foo\\}");
}
TEST(UtilsTest, EscapeStringParens) {
EXPECT_EQ(EscapeString("(foo)"), "\\(foo\\)");
}
TEST(UtilsTest, EscapeStringSquareBrackets) {
EXPECT_EQ(EscapeString("[foo]"), "\\[foo\\]");
}
TEST(UtilsTest, EscapeStringPipe) {
EXPECT_EQ(EscapeString("foo|bar"), "foo\\|bar");
}
TEST(UtilsTest, EscapeStringSlash) {
EXPECT_EQ(EscapeString("/foo/bar"), "\\/foo\\/bar");
}
TEST(UtilsTest, EscapeStringBackslash) {
EXPECT_EQ(EscapeString("\\d"), "\\\\d");
}
} // namespace liburlpattern
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment