26 glob_pattern(std::string_view str) : _tokens(parse(str)), _regex()
28 _regex = make_regex(_tokens);
39 for (
hilet& token : rhs._tokens) {
40 if (
auto string_ = std::get_if<string_type>(&token)) {
42 }
else if (std::holds_alternative<separator_type>(token)) {
44 }
else if (
auto strings_ = std::get_if<strings_type>(&token)) {
46 for (
hilet& s : *strings_) {
51 }
else if (std::holds_alternative<any_char_type>(token)) {
53 }
else if (std::holds_alternative<any_string_type>(token)) {
55 }
else if (std::holds_alternative<any_directory_type>(token)) {
66 return make_regex_string(rhs._tokens);
69 [[nodiscard]]
constexpr std::string base_generic_string()
const noexcept
73 for (
hilet& token : _tokens) {
74 if (
auto string_ = std::get_if<string_type>(&token)) {
76 }
else if (std::holds_alternative<separator_type>(token)) {
78 }
else if (std::holds_alternative<any_directory_type>(token)) {
88 [[nodiscard]] std::filesystem::path base_path()
const noexcept
90 return std::filesystem::path{base_generic_string()};
93 [[nodiscard]]
bool matches(std::string_view path)
const noexcept
98 [[nodiscard]]
bool matches(
std::string const& path)
const noexcept
100 return matches(std::string_view{path});
103 [[nodiscard]]
bool matches(
char const *path)
const noexcept
105 return matches(std::string_view{path});
108 [[nodiscard]]
bool matches(std::filesystem::path
const& path)
const noexcept
110 return matches(path.generic_string());
115 struct separator_type {};
117 struct any_string_type {};
118 struct any_char_type {};
119 struct any_directory_type {};
122 std::variant<string_type, separator_type, strings_type, any_string_type, any_char_type, any_directory_type>;
129 [[nodiscard]]
constexpr static tokens_type parse(
auto first,
auto last)
131#define HI_GLOB_APPEND_STRING() \
133 if (not str.empty()) { \
134 r.emplace_back(std::move(str)); \
139 enum class state_type { idle, star, slash, slash_star, slash_star_star, bracket, brace };
140 using enum state_type;
142 static_assert(std::is_same_v<std::decay_t<
decltype(*first)>,
char>);
144 auto r = tokens_type{};
147 auto str = string_type{};
148 auto strs = strings_type{};
157 HI_GLOB_APPEND_STRING();
161 HI_GLOB_APPEND_STRING();
162 r.emplace_back(any_char_type{});
165 HI_GLOB_APPEND_STRING();
169 HI_GLOB_APPEND_STRING();
173 HI_GLOB_APPEND_STRING();
183 throw parse_error(
"Double /**/ is only allowed between slashes.");
185 r.emplace_back(any_string_type{});
195 r.emplace_back(separator_type{});
203 state = slash_star_star;
205 r.emplace_back(separator_type{});
206 r.emplace_back(any_string_type{});
212 case slash_star_star:
214 r.emplace_back(any_directory_type{});
218 std::format(
"'/**' must end in a slash in glob pattern '{}'", std::string_view{first, last}));
229 strs.emplace_back(1, c);
235 if (not str.empty()) {
243 }
else if (c ==
',') {
261 HI_GLOB_APPEND_STRING();
265 r.emplace_back(any_string_type{});
269 r.emplace_back(separator_type{});
273 r.emplace_back(separator_type{});
274 r.emplace_back(any_string_type{});
277 case slash_star_star:
278 r.emplace_back(any_directory_type{});
282 throw parse_error(
"Unclosed bracket '[' found in glob pattern.");
285 throw parse_error(
"Unclosed brace '{' found in glob pattern.");
290#undef HI_GLOB_APPEND_STRING
293 [[nodiscard]]
constexpr static tokens_type parse(
auto&& range)
295 return parse(std::ranges::begin(range), std::ranges::end(range));
298 [[nodiscard]]
constexpr static std::string make_regex_string(tokens_type
const& tokens)
noexcept
302 for (
hilet& token : tokens) {
303 if (
auto string_ = std::get_if<string_type>(&token)) {
305 }
else if (std::holds_alternative<separator_type>(token)) {
307 }
else if (
auto strings_ = std::get_if<strings_type>(&token)) {
310 for (
hilet& s : *strings_) {
311 if (not std::exchange(first,
false)) {
317 }
else if (std::holds_alternative<any_char_type>(token)) {
319 }
else if (std::holds_alternative<any_string_type>(token)) {
321 }
else if (std::holds_alternative<any_directory_type>(token)) {
330 [[nodiscard]]
static std::regex make_regex(tokens_type
const& tokens)
noexcept
332 return std::regex{make_regex_string(tokens), std::regex_constants::ECMAScript | std::regex_constants::optimize};