LCOV - code coverage report
Current view: top level - http/server/routing - route_pattern.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 3 3
Test Date: 2026-11-10 10:38:07 Functions: 100.0 % 3 3

            Line data    Source code
       1              : #ifndef THINGER_HTTP_ROUTE_PATTERN_HPP
       2              : #define THINGER_HTTP_ROUTE_PATTERN_HPP
       3              : 
       4              : #include <bitset>
       5              : #include <cstdint>
       6              : #include <optional>
       7              : #include <regex>
       8              : #include <span>
       9              : #include <string>
      10              : #include <string_view>
      11              : #include <vector>
      12              : #include <boost/container/small_vector.hpp>
      13              : 
      14              : namespace thinger::http::detail {
      15              : 
      16              : // Part of a route pattern: literal text, or a parameter (:name or :name(regex))
      17              : struct pattern_token {
      18              :     bool parameter = false;
      19              :     std::string text;   // literal text, or parameter name
      20              :     std::string regex;  // parameter regex, empty for :name (any non-empty text without '/')
      21              : };
      22              : 
      23              : // Split a route pattern into literal text and parameters. A parameter is ':' followed by
      24              : // a name ([a-zA-Z_][a-zA-Z0-9_]*) and, optionally, a regex in balanced parentheses.
      25              : // Throws std::invalid_argument if the parentheses of a parameter regex are not balanced.
      26              : std::vector<pattern_token> parse_route_pattern(std::string_view pattern);
      27              : 
      28              : // Whether a parameter regex depends on the text around it: anchors (^, $), word
      29              : // boundaries, lookaheads or back-references
      30              : bool regex_depends_on_context(std::string_view regex);
      31              : 
      32              : // Regex matching the tokens, a capture group per parameter (and the groups of their regexes)
      33              : std::string pattern_regex(std::span<const pattern_token> tokens);
      34              : 
      35              : // Values captured while matching a path, one per parameter, in order. A capture with no
      36              : // data (default constructed) is a parameter that did not participate in the match.
      37              : using route_captures = boost::container::small_vector<std::string_view, 8>;
      38              : 
      39              : // Matcher of a sequence of tokens: one path segment (between slashes), or the rest of the
      40              : // path from a segment on. Parameter regexes made of characters, character classes and
      41              : // quantifiers (e.g. [a-zA-Z0-9_-]{1,32}, [0-9]+, .+, a UUID) are compiled to a sequence
      42              : // of character sets with repetitions, and a lone parameter with alternative words
      43              : // (e.g. properties|buckets) to the list of words, both matched without std::regex; any
      44              : // other regex (groups, anchors...) makes the matcher fall back to std::regex.
      45              : class path_matcher {
      46              : public:
      47              :     explicit path_matcher(std::span<const pattern_token> tokens);
      48              : 
      49              :     // Whether the whole text matches; on success, appends a capture per parameter
      50              :     bool match(std::string_view text, route_captures& captures) const;
      51              : 
      52              :     // Whether a match may include '/' (so it may span several path segments)
      53         2091 :     bool may_match_slash() const { return may_match_slash_; }
      54              : 
      55              :     // A single parameter that may match '/', as :path(.+): matches the rest of the path
      56         2505 :     bool is_catch_all() const { return catch_all_; }
      57              : 
      58              :     // Regex of the tokens, without parameter names: matchers with the same key match
      59              :     // the same texts
      60          840 :     const std::string& key() const { return key_; }
      61              : 
      62              :     // Whether some text may match both matchers: false only when they cannot start with
      63              :     // the same character (a cheap check, true when in doubt)
      64              :     bool may_overlap(const path_matcher& other) const;
      65              : 
      66              : private:
      67              :     static constexpr uint32_t unbounded = UINT32_MAX;
      68              : 
      69              :     // A character of a set, repeated between min and max times
      70              :     struct atom {
      71              :         std::bitset<256> chars;
      72              :         uint32_t min = 1;
      73              :         uint32_t max = 1;
      74              :     };
      75              : 
      76              :     // Parameter matching the atoms [first, last)
      77              :     struct group {
      78              :         uint16_t first;
      79              :         uint16_t last;
      80              :     };
      81              : 
      82              :     static constexpr size_t max_atoms = 64;
      83              : 
      84              :     // Characters a match may start with (all of them if unknown, or if it may be empty)
      85              :     std::bitset<256> first_chars() const;
      86              : 
      87              :     static bool compile_regex(std::string_view regex, std::vector<atom>& atoms);
      88              :     bool match_atoms(size_t index, size_t position, std::string_view text, size_t* starts) const;
      89              : 
      90              :     std::string key_;
      91              :     bool may_match_slash_ = false;
      92              :     bool catch_all_ = false;
      93              : 
      94              :     // Compiled forms
      95              :     std::vector<atom> atoms_;
      96              :     std::vector<group> groups_;
      97              :     std::optional<std::vector<std::string>> words_;
      98              : 
      99              :     // Fallback, with the capture group of each parameter
     100              :     std::optional<std::regex> regex_;
     101              :     std::vector<size_t> regex_groups_;
     102              : };
     103              : 
     104              : } // namespace thinger::http::detail
     105              : 
     106              : #endif // THINGER_HTTP_ROUTE_PATTERN_HPP
        

Generated by: LCOV version 2.0-1