1- #include " endpoint_guessing .h"
1+ #include " endpoint_inferral .h"
22
33#include < cstdint>
44
@@ -8,14 +8,14 @@ namespace {
88
99constexpr size_t MAX_COMPONENTS = 8 ;
1010
11- inline constexpr bool is_digit (char c) noexcept { return c >= ' 0' && c <= ' 9' ; }
12- inline constexpr bool is_hex_alpha (char c) noexcept {
11+ inline constexpr bool is_digit (char c) { return c >= ' 0' && c <= ' 9' ; }
12+ inline constexpr bool is_hex_alpha (char c) {
1313 return (c >= ' a' && c <= ' f' ) || (c >= ' A' && c <= ' F' );
1414}
15- inline constexpr bool is_delim (char c) noexcept {
15+ inline constexpr bool is_delim (char c) {
1616 return c == ' .' || c == ' _' || c == ' -' ;
1717}
18- inline constexpr bool is_str_special (char c) noexcept {
18+ inline constexpr bool is_str_special (char c) {
1919 return c == ' %' || c == ' &' || c == ' \' ' || c == ' (' || c == ' )' ||
2020 c == ' *' || c == ' +' || c == ' ,' || c == ' :' || c == ' =' || c == ' @' ;
2121}
@@ -38,7 +38,7 @@ enum component_type : std::uint8_t {
3838 is_str = 1 << 4 ,
3939};
4040
41- std::string_view to_string (component_type type) noexcept {
41+ StringView to_string (component_type type) {
4242 switch (type) {
4343 case component_type::is_int:
4444 return " {param:int}" ;
@@ -50,16 +50,17 @@ std::string_view to_string(component_type type) noexcept {
5050 return " {param:hex_id}" ;
5151 case component_type::is_str:
5252 return " {param:str}" ;
53- default :
53+ case component_type::none:
54+ // should not be reached
5455 return " " ;
5556 }
57+ // should never reach here
58+ return " " ;
5659}
5760
58- inline uint8_t bool2mask (bool x) noexcept {
59- return static_cast <uint8_t >(-int {x}); // 0 -> 0x00, 1 -> 0xFF
60- }
61+ inline uint8_t bool2mask (bool x) { return x ? 0xFF : 0x00 ; }
6162
62- component_type component_replacement (std::string_view path) noexcept {
63+ component_type component_replacement (StringView path) noexcept {
6364 // viable_components is a bitset of the component types not yet excluded
6465 std::uint8_t viable_components = 0x1F ; // (is_str << 1) - 1
6566 bool found_special_char = false ;
@@ -121,31 +122,26 @@ component_type component_replacement(std::string_view path) noexcept {
121122}
122123} // namespace
123124
124- std::string guess_endpoint (std::string_view orig_path) {
125- auto path = orig_path;
126-
127- // remove the query string if any
128- auto query_pos = path.find (' ?' );
129- if (query_pos != std::string_view::npos) {
130- path = path.substr (0 , query_pos);
131- }
132-
125+ std::string infer_endpoint (StringView path) {
126+ // Expects a clean path without query string (e.g., "/api/users/123")
133127 if (path.empty () || path.front () != ' /' ) {
134128 return " /" ;
135129 }
136130
137131 std::string result{};
138132 size_t component_count = 0 ;
133+ bool final_slash = true ;
139134
140- path.remove_prefix (1 );
135+ path.remove_prefix (1 ); // drop the leading '/'
141136 while (!path.empty ()) {
142137 auto slash_pos = path.find (' /' );
143138
144- std::string_view component = path.substr (0 , slash_pos);
139+ StringView component = path.substr (0 , slash_pos);
145140
146- // remove current component from the path
147- if (slash_pos == std::string_view::npos) {
148- path = std::string_view{};
141+ // remove current component from the path (for the next iteration)
142+ if (slash_pos == StringView::npos) {
143+ path = StringView{};
144+ final_slash = false ;
149145 } else {
150146 path.remove_prefix (slash_pos + 1 );
151147 }
@@ -173,6 +169,10 @@ std::string guess_endpoint(std::string_view orig_path) {
173169 return " /" ;
174170 }
175171
172+ if (final_slash) {
173+ result.append (" /" );
174+ }
175+
176176 return result;
177177}
178178
0 commit comments