RESTinio
Loading...
Searching...
No Matches
path2regex.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
5/*!
6 Utility for converting express.js style routes to regexp.
7
8 Code adopted from https://github.com/pillarjs/path-to-regexp.
9*/
10#pragma once
11
12#include <cassert>
13#include <regex>
14#include <string>
15#include <vector>
16#include <memory>
17#include <functional>
18#include <iterator>
19
20#include <restinio/impl/include_fmtlib.hpp>
21
22#include <restinio/exception.hpp>
23#include <restinio/string_view.hpp>
24
25namespace restinio
26{
27
28namespace path2regex
29{
30
31namespace impl
32{
33
34//
35// escape_group()
36//
37
38//! Escapes not allowed symbols in a sub-match group assigned to a parameter.
39inline auto
40escape_group( const std::string & group )
41{
42 std::string result;
43 result.reserve( group.size() + group.size() / 2 );
44
45 for( const char c : group )
46 {
47 if( '=' == c || '!' == c || ':' == c ||
48 '$' == c || '/' == c || '(' == c || ')' == c )
49 result+= '\\';
50
51 result+= c;
52 }
53
54 return result;
55}
56
57//
58// escape_string()
59//
60
61//! Excape regex control chars.
62inline auto
63escape_string( const std::string & group )
64{
65 std::string result;
66 result.reserve( group.size() + group.size() / 2 );
67
68 for( const char c : group )
69 {
70 if( '.' == c || '+' == c || '*' == c ||
71 '?' == c || '=' == c || '^' == c ||
72 ':' == c || '$' == c || '{' == c ||
73 '}' == c || '(' == c || ')' == c ||
74 '[' == c || ']' == c || '|' == c ||
75 '\\' == c || '/' == c )
76 result+= '\\';
77
78 result+= c;
79 }
80
81 return result;
82}
83
84} /* namespace impl */
85
86//
87// options_t
88//
89
90//! Options for matching routes.
92{
93 public:
94 options_t &
95 sensitive( bool s ) &
96 {
97 m_sensitive = s;
98 return *this;
99 }
100
101 options_t &&
102 sensitive( bool s ) &&
103 {
104 return std::move( this->sensitive( s ) );
105 }
106
107 bool
108 sensitive() const
109 {
110 return m_sensitive;
111 }
112
113 options_t &
114 strict( bool p ) &
115 {
116 m_strict = p;
117 return *this;
118 }
119
120 options_t &&
121 strict( bool p ) &&
122 {
123 return std::move( this->strict( p ) );
124 }
125
126 bool
127 strict() const
128 {
129 return m_strict;
130 }
131
132 options_t &
133 ending( bool p ) &
134 {
135 m_ending = p;
136 return *this;
137 }
138
139 options_t &&
140 ending( bool p ) &&
141 {
142 return std::move( this->ending( p ) );
143 }
144
145 bool
146 ending() const
147 {
148 return m_ending;
149 }
150
151 options_t &
152 delimiter( std::string p ) &
153 {
154 m_delimiter = std::move( p );
155 return *this;
156 }
157
158 options_t &&
159 delimiter( std::string p ) &&
160 {
161 return std::move( this->delimiter( std::move( p ) ) );
162 }
163
164 const std::string &
165 delimiter() const
166 {
167 return m_delimiter;
168 }
169
170 std::string
171 make_delimiter( std::string d ) const
172 {
173 std::string result{ std::move( d ) };
174
175 if( result.empty() )
176 result = delimiter();
177
178 return result;
179 }
180
181 options_t &
182 delimiters( std::string p ) &
183 {
184 m_delimiters = std::move( p );
185 return *this;
186 }
187
188 options_t &&
189 delimiters( std::string p ) &&
190 {
191 return std::move( this->delimiters( std::move( p ) ) );
192 }
193
194 const std::string &
196 {
197 return m_delimiters;
198 }
199
200 options_t &
201 ends_with( std::vector< std::string > p ) &
202 {
203 m_ends_with = std::move( p );
204 return *this;
205 }
206
207 options_t &&
208 ends_with( std::vector< std::string > p ) &&
209 {
210 return std::move( this->ends_with( std::move( p ) ) );
211 }
212
213 const std::vector< std::string > &
214 ends_with() const
215 {
216 return m_ends_with;
217 }
218
219 std::string
221 {
222 std::string result;
223
224 for( const auto & e : m_ends_with )
225 {
226 if( !e.empty() )
227 {
228 result += impl::escape_string( e ) + "|";
229 }
230 }
231
232 result += "$";
233
234 return result;
235 }
236
237 private:
238 //! When true the route will be case sensitive.
239 bool m_sensitive{ false };
240
241 //! When false the trailing slash is optional.
242 bool m_strict{ false };
243
244 //! When false the path will match at the beginning.
245 bool m_ending{ true };
246
247 //! Path delimiter.
248 std::string m_delimiter{ "/" };
249
250 //! Path delimiters.
251 std::string m_delimiters{ "./" };
252
253 //! Path delimiter.
255};
256
257//
258// param_appender_t
259//
260
261//! Appends sub-match as a request parameter to specified container.
262template < typename Route_Param_Appender >
264 std::function< void ( Route_Param_Appender &, string_view_t ) >;
265
266//
267// param_appender_sequence_t
268//
269
270//! A sequence of appenders for submatches.
271template < typename Route_Param_Appender >
273
274//
275// make_param_setter
276//
277
278//! Create default appender for named parameter.
279template < typename Route_Param_Appender >
281make_param_setter( string_view_t key )
282{
283 return
284 [ key ](
285 Route_Param_Appender & parameters,
286 string_view_t value ){
287 parameters.add_named_param( key, value );
288 };
289}
290
291//! Create default appender indexed parameter.
292template < typename Route_Param_Appender >
294make_param_setter( std::size_t )
295{
296 return
297 []( Route_Param_Appender & parameters, string_view_t value ){
298 parameters.add_indexed_param( value );
299 };
300}
301
302namespace impl
303{
304
305//
306// string_view_buffer_storage_appender_t
307//
308
309//! Appender for names to a given buffered string.
310template < typename Container >
311class string_view_buffer_storage_appender_t final
312{
313 public:
314 string_view_buffer_storage_appender_t( std::size_t reserve_size, Container & buffer )
315 : m_buffer{ buffer }
316 {
317 m_buffer.reserve( reserve_size );
318 assert( m_buffer.capacity() >= reserve_size );
319 }
320
321 //! Appends a given name to buffer,
322 //! and returns a string view object within the context of a buffer.
324 append_name( const std::string & name )
325 {
326 const auto n = name.size();
327 if( m_buffer.capacity() - m_buffer.size() < n )
328 {
329 // This actually should never happen,
330 // because buffer is set to the size
331 // of a whole route-path that itself contains all the names.
332 throw exception_t{ "unable to insert data into names buffer" };
333 }
334
335 // Remember where previous names finishes.
336 const auto prev_size = m_buffer.size();
337
338 std::copy( name.data(), name.data() + n, std::back_inserter( m_buffer ) );
339 return string_view_t{ m_buffer.data() + prev_size, n };
340 }
341
342 //! A stub for indexed paramaters.
343 std::size_t
344 append_name( std::size_t i ) const
345 {
346 return i;
347 }
348
349 private:
350 Container & m_buffer;
351};
352
353using names_buffer_appender_t = string_view_buffer_storage_appender_t< std::string >;
354
355//! The main path matching expression.
356constexpr auto path_regex_str =
357 R"((\\.)|(?:\:(\w+)(?:\‍(((?:\\.|[^\\‍()])+)\‍))?|\‍(((?:\\.|[^\\‍()])+)\‍))([+*?])?)";
358
364
365//
366// token_t
367//
368
369//! Base class for token variants.
370template < typename Route_Param_Appender >
372{
373 public:
374 token_t() = default;
375 token_t( const token_t & ) = delete;
376 token_t( token_t && ) = delete;
377 virtual ~token_t() = default;
378
379 virtual token_type_t
381 std::string & route,
382 param_appender_sequence_t< Route_Param_Appender > & param_appender_sequence,
383 names_buffer_appender_t & names_buffer_appender ) const = 0;
384
385 virtual bool
386 is_end_delimited( const std::string & ) const noexcept
387 {
388 return false;
389 }
390};
391
392template < typename Route_Param_Appender >
394
395template < typename Route_Param_Appender >
397
398//
399// plain_string_token_t
400//
401
402//! Plain str token.
403template < typename Route_Param_Appender >
404class plain_string_token_t final : public token_t< Route_Param_Appender >
405{
406 public:
407 plain_string_token_t( const std::string & path )
409 , m_last_char{ path.back() }
410 {}
411
412 virtual token_type_t
414 std::string & route,
415 param_appender_sequence_t< Route_Param_Appender > &,
416 names_buffer_appender_t & ) const override
417 {
418 route += m_escaped_path;
419
421 }
422
423 virtual bool
424 is_end_delimited( const std::string & delimiters ) const noexcept override
425 {
426 return std::string::npos != delimiters.find( m_last_char );
427 }
428
429 private:
430 //! Already escaped piece of the route.
431 const std::string m_escaped_path;
432 const char m_last_char;
433};
434
435template < typename Route_Param_Appender >
437create_token( std::string path )
438{
439 using token_t = plain_string_token_t< Route_Param_Appender >;
440 return std::make_unique< token_t >( std::move( path ) );
441}
442
443//
444// parameter_token_t
445//
446
447//! Token for paramater (named/indexed).
448template < typename Route_Param_Appender, typename Name >
449class parameter_token_t final : public token_t< Route_Param_Appender >
450{
451 public:
452 parameter_token_t( const parameter_token_t & ) = delete;
453 parameter_token_t( parameter_token_t && ) = delete;
454
456 Name name,
457 const std::string & prefix,
458 std::string delimiter,
459 bool optional,
460 bool repeat,
461 bool partial,
462 std::string pattern )
463 : m_name{ std::move( name ) }
466 , m_optional{ optional }
467 , m_repeat{ repeat }
468 , m_partial{ partial }
469 , m_pattern{ std::move( pattern ) }
470 {}
471
472 virtual token_type_t
474 std::string & route,
475 param_appender_sequence_t< Route_Param_Appender > & param_appender_sequence,
476 names_buffer_appender_t & names_buffer_appender ) const override
477 {
478 // Basic capturing pattern.
479 auto capture = "(?:" + m_pattern + ")";
480
481 if( m_repeat )
482 {
483 // Add * as the parameter can be repeeated.
484 capture += "(?:" + m_escaped_prefix + capture + ")*";
485 }
486
487 if( m_optional )
488 {
489 // Optional param goes in ()?.
490 if( !m_partial )
491 {
492 capture = "(?:" + m_escaped_prefix + "(" + capture + "))?";
493 }
494 else
495 {
496 capture = m_escaped_prefix + "(" + capture + ")?";
497 }
498 }
499 else
500 {
501 // Mandatory param goes in ().
502 capture = m_escaped_prefix + "(" + capture + ")";
503 }
504
505 route += capture;
506
507 param_appender_sequence.push_back(
508 make_param_setter< Route_Param_Appender >(
509 names_buffer_appender.append_name( m_name ) ) );
510
512 }
513
514 private:
515 const Name m_name;
516 const std::string m_escaped_prefix;
517 const std::string m_delimiter;
518 const bool m_optional;
519 const bool m_repeat;
520 const bool m_partial;
521 const std::string m_pattern;
522};
523
524//
525// create_token()
526//
527
528//! Creates tokent for specific parameter.
529template < typename Route_Param_Appender, typename Name >
532 Name name,
533 std::string prefix,
534 std::string delimiter,
535 bool optional,
536 bool repeat,
537 bool partial,
538 std::string pattern )
539{
540 return std::make_unique< parameter_token_t< Route_Param_Appender, Name > >(
541 std::move( name ),
542 std::move( prefix ),
543 std::move( delimiter ),
544 optional,
545 repeat,
546 partial,
547 std::move( pattern ) );
548}
549
550//! Indexes for different groups in matched result
551//! (used when extracting tokens from initial route).
552//! \{
553constexpr std::size_t group_escaped_idx = 1;
554constexpr std::size_t group_name_idx = 2;
555constexpr std::size_t group_capture_idx = 3;
556constexpr std::size_t group_group_idx = 4;
557constexpr std::size_t group_modifier_idx = 5;
558//! \}
559
560//! Checks that string doesn't contain non-excaped brackets
561inline std::string
562check_no_unescaped_brackets( string_view_t strv, std::size_t base_pos )
563{
564 auto pos = strv.find( '(' );
565 if( std::string::npos != pos )
566 {
567 throw exception_t{
568 fmt::format(
570 "non-escaped bracket '(' at pos {}: may be unmatched group start" ),
571 base_pos + pos ) };
572 }
573
574 pos = strv.find( ')' );
575 if( std::string::npos != pos )
576 {
577 throw exception_t{
578 fmt::format(
580 "non-escaped bracket ')' at pos {}: may be unmatched group finish" ),
581 base_pos + pos ) };
582 }
583
584 return std::string{ strv.data(), strv.size() };
585}
586
587//
588// handle_param_token()
589//
590
591//! Handling of a parameterized token.
592template < typename Route_Param_Appender, typename MATCH >
593inline void
595 const options_t & options,
596 const MATCH & match,
597 std::string & path,
598 bool & path_escaped,
599 token_list_t< Route_Param_Appender > & result )
600{
601 std::string prefix{ "" }; // prev in js code.
602 if( !path_escaped && !path.empty() )
603 {
604 const auto k = path.size() - 1;
605
606 if( std::string::npos != options.delimiters().find( path[k] ) )
607 {
608 prefix = path.substr( k, 1 );
609 path = path.substr( 0, k );
610 }
611 }
612
613 // Push the current path onto the tokens.
614 if( !path.empty() )
615 {
616 result.push_back( create_token< Route_Param_Appender >( std::move( path ) ) );
617 path_escaped = false;
618 }
619
620 const auto next = match.suffix().str().substr( 0, 1 );
621
622 std::string name{ match[ group_name_idx ].str() };
623 const std::string modifier{ match[ group_modifier_idx ].str() };
624
625 const bool partial = !prefix.empty() && !next.empty() && prefix != next;
626
627 const bool optional = modifier == "?" || modifier == "*";
628 const bool repeat = modifier == "+" || modifier == "*";
629 std::string delimiter = options.make_delimiter( prefix );
630
631 auto create_pattern = [ delimiter ]( auto pattern ){
632 if( !pattern.empty() )
633 {
634 pattern = escape_group( pattern );
635 }
636 else
637 {
638 pattern = "[^" + escape_string( delimiter ) + "]+?";
639 }
640 return pattern;
641 };
642
643 if( !name.empty() )
644 {
645 // Named parameter.
646 result.push_back(
647 create_token< Route_Param_Appender >(
648 name,
649 std::move( prefix ),
650 std::move( delimiter ),
651 optional,
652 repeat,
653 partial,
654 create_pattern( match[ group_capture_idx ].str() ) ) );
655 }
656 else
657 {
658 // Indexed parameter.
659 result.push_back(
660 create_token< Route_Param_Appender >(
661 std::size_t{ 0 }, // just to have a variable of this type.
662 std::move( prefix ),
663 std::move( delimiter ),
664 optional,
665 repeat,
666 partial,
667 create_pattern( match[ group_group_idx ].str() ) ) );
668 }
669}
670
671//
672// parse()
673//
674
675//! Parse a string for the raw tokens.
676template < typename Route_Param_Appender >
678parse( string_view_t route_sv, const options_t & options )
679{
680 token_list_t< Route_Param_Appender > result;
681
682 std::string path{};
683 const std::regex main_path_regex{ path_regex_str };
684 bool path_escaped = false;
685
686 std::cregex_iterator token_it{
687 route_sv.data(),
688 route_sv.data() + route_sv.size(),
689 main_path_regex
690 };
691 std::cregex_iterator token_end{};
692
693 if( token_it == token_end )
694 {
695 // Path is a single token.
696 path = check_no_unescaped_brackets( route_sv, 0 );
697 }
698
699 while( token_it != token_end )
700 {
701 const auto & match = *token_it;
702
703 assert( 6 == match.size() );
704
705 const string_view_t prefix{
706 match.prefix().first,
707 static_cast<std::size_t>( match.prefix().length() ) };
708
709 path += check_no_unescaped_brackets( prefix,
710 static_cast<std::size_t>(match.position()) - prefix.size() );
711
712 const auto escaped = match[ group_escaped_idx ].str();
713 if( !escaped.empty() )
714 {
715 assert( 2 == escaped.size() );
716 path += escaped[ 1 ];
717 path_escaped = true;
718 }
719 else
720 {
721 handle_param_token( options, match, path, path_escaped, result );
722 }
723
724 auto next_it = token_it;
725 std::advance( next_it, 1 );
726
727 if( next_it == token_end )
728 {
729 const std::string suffix{ match.suffix() };
730 path +=
731 check_no_unescaped_brackets(
732 suffix,
733 static_cast<std::size_t>(match.position() + match.length()) );
734 }
735
736 token_it = next_it;
737 }
738
739 if( !path.empty() )
740 result.push_back( create_token< Route_Param_Appender >( std::move( path ) ) );
741
742 return result;
743}
744
745//
746// route_regex_matcher_data_t
747//
748
749//! Resulting regex and param extraction for a specific route.
750template < typename Route_Param_Appender, typename Regex_Engine >
752{
756
759
760 using regex_t = typename Regex_Engine::compiled_regex_t;
761
763
764 //! Char buffer for holding named paramaters.
765 /*!
766 In order to store named parameters 'names' in a continous block of memory
767 and use them in param_appender_sequence items as string_view.
768 */
769 std::shared_ptr< std::string > m_named_params_buffer;
770
771 //! Appenders for captured values (names/indexed groups).
773};
774
775//
776// tokens2regexp()
777//
778
779//! Makes route regex matcher out of path tokens.
780template < typename Route_Param_Appender, typename Regex_Engine >
781auto
783 string_view_t path,
784 const token_list_t< Route_Param_Appender > & tokens,
785 const options_t & options )
786{
787 route_regex_matcher_data_t< Route_Param_Appender, Regex_Engine > result;
788 try
789 {
790 result.m_named_params_buffer = std::make_shared< std::string >();
792 names_buffer_appender{ path.size(), *result.m_named_params_buffer };
793
794 std::string route;
795 auto & param_appender_sequence = result.m_param_appender_sequence;
796
797 // The number of capture groups in resultin regex
798 // 1 is for match of a route itself.
799 std::size_t captured_groups_count = 1 ;
800
801 for( const auto & t : tokens )
802 {
803 const auto appended_token_type =
804 t->append_self_to( route, param_appender_sequence, names_buffer_appender );
805
806 if( token_type_t::capturing_token == appended_token_type )
807 ++captured_groups_count;
808 }
809
810 if( Regex_Engine::max_capture_groups() < captured_groups_count )
811 {
812 // This number of captures is not possible with this engine.
813 throw exception_t{
814 fmt::format(
816 "too many parameter to capture from route: {}, while {} "
817 "is the maximum" ),
818 captured_groups_count,
819 Regex_Engine::max_capture_groups() ) };
820 }
821
822 const auto & delimiter = escape_string( options.delimiter() );
823 const auto & ends_with = options.make_ends_with();
824
825 if( options.ending() )
826 {
827 if( !options.strict() )
828 {
829 route += "(?:" + delimiter + ")?";
830 }
831
832 if( ends_with == "$" )
833 route += '$';
834 else
835 route += "(?=" + ends_with + ")";
836 }
837 else
838 {
839 if( !options.strict() )
840 route += "(?:" + delimiter + "(?=" + ends_with + "))?";
841
842 if( !tokens.empty() &&
843 !tokens.back()->is_end_delimited( options.delimiters() ) )
844 route += "(?=" + delimiter + "|" + ends_with + ")";
845 }
846
847 result.m_regex = Regex_Engine::compile_regex( "^" + route, options.sensitive() );
848 }
849 catch( const std::exception & ex )
850 {
851 throw exception_t{
852 fmt::format(
853 RESTINIO_FMT_FORMAT_STRING( "unable to process route \"{}\": {}" ),
854 fmtlib_tools::streamed( path ), ex.what() ) };
855 }
856
857 return result;
858}
859
860} /* namespace impl */
861
862//
863// path2regex()
864//
865
866//! The main path matching regexp.
867template < typename Route_Param_Appender, typename Regex_Engine >
868inline auto
870 string_view_t path,
871 const options_t & options )
872{
873 return impl::tokens2regexp< Route_Param_Appender, Regex_Engine >(
874 path,
875 impl::parse< Route_Param_Appender >( path, options ),
876 options );
877}
878
879} /* namespace path2regex */
880
881} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
parameter_token_t(const parameter_token_t &)=delete
parameter_token_t(Name name, const std::string &prefix, std::string delimiter, bool optional, bool repeat, bool partial, std::string pattern)
parameter_token_t(parameter_token_t &&)=delete
virtual token_type_t append_self_to(std::string &route, param_appender_sequence_t< Route_Param_Appender > &param_appender_sequence, names_buffer_appender_t &names_buffer_appender) const override
const std::string m_escaped_path
Already escaped piece of the route.
virtual token_type_t append_self_to(std::string &route, param_appender_sequence_t< Route_Param_Appender > &, names_buffer_appender_t &) const override
virtual bool is_end_delimited(const std::string &delimiters) const noexcept override
string_view_t append_name(const std::string &name)
Appends a given name to buffer, and returns a string view object within the context of a buffer.
std::size_t append_name(std::size_t i) const
A stub for indexed paramaters.
string_view_buffer_storage_appender_t(std::size_t reserve_size, Container &buffer)
Base class for token variants.
virtual bool is_end_delimited(const std::string &) const noexcept
virtual token_type_t append_self_to(std::string &route, param_appender_sequence_t< Route_Param_Appender > &param_appender_sequence, names_buffer_appender_t &names_buffer_appender) const =0
token_t(const token_t &)=delete
Options for matching routes.
options_t & ends_with(std::vector< std::string > p) &
const std::vector< std::string > & ends_with() const
options_t && delimiter(std::string p) &&
bool m_strict
When false the trailing slash is optional.
const std::string & delimiter() const
options_t && delimiters(std::string p) &&
std::string make_ends_with() const
options_t & strict(bool p) &
options_t & sensitive(bool s) &
options_t && ending(bool p) &&
std::string m_delimiters
Path delimiters.
bool m_sensitive
When true the route will be case sensitive.
std::vector< std::string > m_ends_with
Path delimiter.
bool m_ending
When false the path will match at the beginning.
options_t & delimiters(std::string p) &
const std::string & delimiters() const
options_t & delimiter(std::string p) &
std::string make_delimiter(std::string d) const
options_t & ending(bool p) &
options_t && ends_with(std::vector< std::string > p) &&
options_t && sensitive(bool s) &&
options_t && strict(bool p) &&
std::string m_delimiter
Path delimiter.
#define RESTINIO_FMT_FORMAT_STRING(s)
constexpr std::size_t group_escaped_idx
Indexes for different groups in matched result (used when extracting tokens from initial route).
auto escape_string(const std::string &group)
Excape regex control chars.
string_view_buffer_storage_appender_t< std::string > names_buffer_appender_t
auto tokens2regexp(string_view_t path, const token_list_t< Route_Param_Appender > &tokens, const options_t &options)
Makes route regex matcher out of path tokens.
constexpr std::size_t group_capture_idx
token_unique_ptr_t< Route_Param_Appender > create_token(std::string path)
auto escape_group(const std::string &group)
Escapes not allowed symbols in a sub-match group assigned to a parameter.
constexpr std::size_t group_modifier_idx
constexpr std::size_t group_name_idx
std::string check_no_unescaped_brackets(string_view_t strv, std::size_t base_pos)
Checks that string doesn't contain non-excaped brackets.
constexpr auto path_regex_str
The main path matching expression.
constexpr std::size_t group_group_idx
void handle_param_token(const options_t &options, const MATCH &match, std::string &path, bool &path_escaped, token_list_t< Route_Param_Appender > &result)
Handling of a parameterized token.
token_list_t< Route_Param_Appender > parse(string_view_t route_sv, const options_t &options)
Parse a string for the raw tokens.
token_unique_ptr_t< Route_Param_Appender > create_token(Name name, std::string prefix, std::string delimiter, bool optional, bool repeat, bool partial, std::string pattern)
Creates tokent for specific parameter.
auto path2regex(string_view_t path, const options_t &options)
The main path matching regexp.
param_appender_t< Route_Param_Appender > make_param_setter(std::size_t)
Create default appender indexed parameter.
param_appender_t< Route_Param_Appender > make_param_setter(string_view_t key)
Create default appender for named parameter.
std::function< void(Route_Param_Appender &, string_view_t) > param_appender_t
Appends sub-match as a request parameter to specified container.
Resulting regex and param extraction for a specific route.
std::shared_ptr< std::string > m_named_params_buffer
Char buffer for holding named paramaters.
param_appender_sequence_t< Route_Param_Appender > m_param_appender_sequence
Appenders for captured values (names/indexed groups).
typename Regex_Engine::compiled_regex_t regex_t
route_regex_matcher_data_t(route_regex_matcher_data_t &&)=default
route_regex_matcher_data_t & operator=(route_regex_matcher_data_t &&)=delete
route_regex_matcher_data_t & operator=(const route_regex_matcher_data_t &)=delete
route_regex_matcher_data_t(const route_regex_matcher_data_t &)=delete