1 /*
2 * RESTinio
3 */
4
5 /*!
6 * @file
7 * @brief Utilities for parsing values of http-fields
8 *
9 * @since v.0.6.1
10 */
11
12 #pragma once
13
14 #include <restinio/impl/string_caseless_compare.hpp>
15
16 #include <restinio/helpers/easy_parser.hpp>
17
18 #include <restinio/expected.hpp>
19
20 #include <algorithm>
21
22 namespace restinio
23 {
24
25 namespace http_field_parsers
26 {
27
28 using namespace restinio::easy_parser;
29
30 namespace meta = restinio::utils::metaprogramming;
31
32 namespace qvalue_details
33 {
34
35 //! A type to hold a qvalue.
36 using underlying_uint_t = std::uint_least16_t;
37
38 //! The maximal allowed value for a qvalue.
39 constexpr underlying_uint_t maximum = 1000u;
40 //! The minimal allowed value for a qvalue.
41 constexpr underlying_uint_t zero = 0u;
42
43 // A part of workaround for problem with static constexpr members
44 // detected after release of v.0.6.1.
45 enum class extremum_min_t { v };
46 enum class extremum_max_t { v };
47
48 //! A helper wrapper to indicate that value is already checked and
49 //! shouldn't be checked again.
50 class trusted
51 {
52 const underlying_uint_t m_value;
53
54 public :
55 explicit constexpr
trusted(underlying_uint_t value)56 trusted( underlying_uint_t value ) noexcept : m_value{ value } {}
57
get() const58 constexpr auto get() const noexcept { return m_value; }
59 };
60
61 //! A helper wrapper to indicate that value hasn't been checked yet
62 //! and should be checked in the constructor of qvalue.
63 class untrusted
64 {
65 underlying_uint_t m_value;
66
67 public :
68 explicit
untrusted(underlying_uint_t value)69 untrusted( underlying_uint_t value ) : m_value{ value }
70 {
71 if( m_value > maximum )
72 throw exception_t( "invalid value for "
73 "http_field_parser::rfc::qvalue_t" );
74 }
75
get() const76 auto get() const noexcept { return m_value; }
77 };
78
79 } /* namespace qvalue_details */
80
81 //
82 // qvalue_t
83 //
84 /*!
85 * @brief A class for holding the parsed value of qvalue from RFC7231.
86 *
87 * An important note: qvalue in RFC7231 is defined as a number with
88 * fractional point. This number can have no more that three digits
89 * after a point. And the non-factional part can only be 0 or 1. If
90 * non-frational part is 1 then fractional part can only be 0. It means
91 * that qvalue can be in the range [0.000, 1.000].
92 *
93 * To simplify work with qvalue RESTinio holds qvalue as a small integer
94 * in the range [0, 1000] where 0 corresponds to 0.000 and 1000 corresponds
95 * to 1.000. In means, for example, that value 0.05 will be represented and 50,
96 * and the value 0.001 will be represented as 1, and the value 0.901 will
97 * be represented as 901.
98 *
99 * The integer representation of qvalue can be obtained via as_uint()
100 * method.
101 *
102 * Method as_string() returns std::string with number with fractional
103 * part inside. It means:
104 * @code
105 * assert("1.000" == qvalue_t{qvalue_t::maximum}.as_string());
106 * assert("0.901" == qvalue_t{qvalue_t::untrusted{901}}.as_string());
107 * @endcode
108 * Such representation is also used in `operator<<` for std::ostream.
109 *
110 * Instances of qvalue_t can be compared, operations `==`, `!=`, `<` and `<=`
111 * are supported.
112 *
113 * There are two ways to construct a new qvalue object.
114 *
115 * The first and recommended way is intended for cases when the source
116 * value for a new qvalue object isn't known at the compile-time and
117 * produced somehow at the run-time. It's the usage of qvalue_t::untrusted
118 * wrapper:
119 * @code
120 * qvalue_t weight{qvalue_t::untrusted{config.default_weigh() * 10}};
121 * @endcode
122 * In that case the value of `untrusted` will be checked in
123 * qvalue_t::untrusted's constructor and an exception will be thrown if that
124 * value isn't in [0, 1000] range.
125 *
126 * The second way is intended to be used with compile-time constants:
127 * @code
128 * qvalue_t weight{qvalue_t::trusted{250}};
129 * @endcode
130 * In the case of `trusted` the value is not checked in qvalue_t's
131 * constructor. Therefore that way should be used with additional care.
132 *
133 * @since v.0.6.1
134 */
135 class qvalue_t
136 {
137 public :
138 //! The type of underlying small integer.
139 using underlying_uint_t = qvalue_details::underlying_uint_t;
140 //! The type that doesn't check a value to be used for qvalue.
141 using trusted = qvalue_details::trusted;
142 //! The type that checks a value to be used for qvalue.
143 using untrusted = qvalue_details::untrusted;
144
145 //! The indicator that tells that new qvalue object should have
146 //! the maximal allowed value.
147 static constexpr qvalue_details::extremum_max_t maximum =
148 qvalue_details::extremum_max_t::v;
149 //! The indicator that tells that new qvalue object should have
150 //! the minimal allowed value.
151 static constexpr qvalue_details::extremum_min_t zero =
152 qvalue_details::extremum_min_t::v;
153
154 private :
155 // Note: with the terminal 0-symbol.
156 using underlying_char_array_t = std::array<char, 6>;
157
158 underlying_uint_t m_value{};
159
160 underlying_char_array_t
make_char_array() const161 make_char_array() const noexcept
162 {
163 underlying_char_array_t result;
164 if( qvalue_details::maximum == m_value )
165 {
166 std::strcpy( &result[0], "1.000" );
167 }
168 else
169 {
170 result[0] = '0';
171 result[1] = '.';
172
173 result[2] = '0' + static_cast<char>(m_value / 100u);
174 const auto d2 = m_value % 100u;
175 result[3] = '0' + static_cast<char>(d2 / 10u);
176 const auto d3 = d2 % 10u;
177 result[4] = '0' + static_cast<char>(d3);
178 result[5] = 0;
179 }
180
181 return result;
182 }
183
184 public :
185
186 constexpr qvalue_t() = default;
187
qvalue_t(untrusted val)188 qvalue_t( untrusted val ) noexcept
189 : m_value{ val.get() }
190 {}
191
qvalue_t(trusted val)192 constexpr qvalue_t( trusted val ) noexcept
193 : m_value{ val.get() }
194 {}
195
qvalue_t(qvalue_details::extremum_min_t)196 constexpr qvalue_t( qvalue_details::extremum_min_t ) noexcept
197 : m_value{ qvalue_details::zero }
198 {}
199
qvalue_t(qvalue_details::extremum_max_t)200 constexpr qvalue_t( qvalue_details::extremum_max_t ) noexcept
201 : m_value{ qvalue_details::maximum }
202 {}
203
as_uint() const204 constexpr auto as_uint() const noexcept { return m_value; }
205
as_string() const206 auto as_string() const
207 {
208 return std::string{ &make_char_array().front() };
209 }
210
211 friend std::ostream &
operator <<(std::ostream & to,const qvalue_t & what)212 operator<<( std::ostream & to, const qvalue_t & what )
213 {
214 return (to << &what.make_char_array().front());
215 }
216 };
217
218 RESTINIO_NODISCARD
219 inline bool
operator ==(const qvalue_t & a,const qvalue_t & b)220 operator==( const qvalue_t & a, const qvalue_t & b ) noexcept
221 {
222 return a.as_uint() == b.as_uint();
223 }
224
225 RESTINIO_NODISCARD
226 inline bool
operator !=(const qvalue_t & a,const qvalue_t & b)227 operator!=( const qvalue_t & a, const qvalue_t & b ) noexcept
228 {
229 return a.as_uint() != b.as_uint();
230 }
231
232 RESTINIO_NODISCARD
233 inline bool
operator <(const qvalue_t & a,const qvalue_t & b)234 operator<( const qvalue_t & a, const qvalue_t & b ) noexcept
235 {
236 return a.as_uint() < b.as_uint();
237 }
238
239 RESTINIO_NODISCARD
240 inline bool
operator <=(const qvalue_t & a,const qvalue_t & b)241 operator<=( const qvalue_t & a, const qvalue_t & b ) noexcept
242 {
243 return a.as_uint() <= b.as_uint();
244 }
245
246 namespace impl
247 {
248
249 using namespace restinio::easy_parser::impl;
250
251 //
252 // is_alpha
253 //
254 /*!
255 * @brief Is a character an ALPHA?
256 *
257 * See: https://tools.ietf.org/html/rfc5234#appendix-B.1
258 *
259 * @since v.0.6.1
260 */
261 RESTINIO_NODISCARD
262 inline constexpr bool
is_alpha(const char ch)263 is_alpha( const char ch ) noexcept
264 {
265 return (ch >= '\x41' && ch <= '\x5A') ||
266 (ch >= '\x61' && ch <= '\x7A');
267 }
268
269 //
270 // is_alpha_predicate_t
271 //
272 /*!
273 * @brief A preducate for symbol_producer_template that checks that
274 * a symbol is an alpha.
275 *
276 * @since v.0.6.2
277 */
278 struct is_alpha_predicate_t
279 {
280 RESTINIO_NODISCARD
281 bool
operator ()restinio::http_field_parsers::impl::is_alpha_predicate_t282 operator()( const char actual ) const noexcept
283 {
284 return is_alpha(actual);
285 }
286 };
287
288 //
289 // is_alphanum_predicate_t
290 //
291 /*!
292 * @brief A preducate for symbol_producer_template that checks that
293 * a symbol is an alpha or numeric.
294 *
295 * @since v.0.6.2
296 */
297 struct is_alphanum_predicate_t
298 {
299 RESTINIO_NODISCARD
300 bool
operator ()restinio::http_field_parsers::impl::is_alphanum_predicate_t301 operator()( const char actual ) const noexcept
302 {
303 return is_alpha(actual) || is_digit(actual);
304 }
305 };
306
307 //
308 // is_vchar
309 //
310 /*!
311 * @brief Is a character a VCHAR?
312 *
313 * See: https://tools.ietf.org/html/rfc5234#appendix-B.1
314 *
315 * @since v.0.6.1
316 */
317 RESTINIO_NODISCARD
318 inline constexpr bool
is_vchar(const char ch)319 is_vchar( const char ch ) noexcept
320 {
321 return (ch >= '\x21' && ch <= '\x7E');
322 }
323
324 //
325 // is_vchar_predicate_t
326 //
327 /*!
328 * @brief A preducate for symbol_producer_template that checks that
329 * a symbol is a VCHAR.
330 *
331 * @since v.0.6.2
332 */
333 struct is_vchar_predicate_t
334 {
335 RESTINIO_NODISCARD
336 bool
operator ()restinio::http_field_parsers::impl::is_vchar_predicate_t337 operator()( const char actual ) const noexcept
338 {
339 return is_vchar(actual);
340 }
341 };
342
343 //
344 // is_obs_text
345 //
346 /*!
347 * @brief Is a character an obs_text?
348 *
349 * See: https://tools.ietf.org/html/rfc7230
350 *
351 * @since v.0.6.1
352 */
353 RESTINIO_NODISCARD
354 inline constexpr bool
is_obs_text(const char ch)355 is_obs_text( const char ch ) noexcept
356 {
357 constexpr unsigned short left = 0x80u;
358 constexpr unsigned short right = 0xFFu;
359
360 const unsigned short t = static_cast<unsigned short>(
361 static_cast<unsigned char>(ch));
362
363 return (t >= left && t <= right);
364 }
365
366 //
367 // is_qdtext
368 //
369 /*!
370 * @brief Is a character a qdtext?
371 *
372 * See: https://tools.ietf.org/html/rfc7230
373 *
374 * @since v.0.6.1
375 */
376 RESTINIO_NODISCARD
377 inline constexpr bool
is_qdtext(const char ch)378 is_qdtext( const char ch ) noexcept
379 {
380 return ch == SP ||
381 ch == HTAB ||
382 ch == '!' ||
383 (ch >= '\x23' && ch <= '\x5B') ||
384 (ch >= '\x5D' && ch <= '\x7E') ||
385 is_obs_text( ch );
386 }
387
388 //
389 // is_ctext
390 //
391 /*!
392 * @brief Is a character a ctext?
393 *
394 * See: https://tools.ietf.org/html/rfc7230
395 *
396 * @since v.0.6.4
397 */
398 RESTINIO_NODISCARD
399 inline constexpr bool
is_ctext(const char ch)400 is_ctext( const char ch ) noexcept
401 {
402 return ch == SP ||
403 ch == HTAB ||
404 (ch >= '\x21' && ch <= '\x27') ||
405 (ch >= '\x2A' && ch <= '\x5B') ||
406 (ch >= '\x5D' && ch <= '\x7E') ||
407 is_obs_text( ch );
408 }
409
410 //
411 // is_ctext_predicate_t
412 //
413 /*!
414 * @brief A preducate for symbol_producer_template that checks that
415 * a symbol is a ctext.
416 *
417 * @since v.0.6.4
418 */
419 struct is_ctext_predicate_t
420 {
421 RESTINIO_NODISCARD
422 bool
operator ()restinio::http_field_parsers::impl::is_ctext_predicate_t423 operator()( const char actual ) const noexcept
424 {
425 return is_ctext(actual);
426 }
427 };
428
429 //
430 // is_token_char_predicate_t
431 //
432 /*!
433 * @brief A predicate for symbol_producer_template that checks that
434 * a symbol can be used inside a token.
435 */
436 struct is_token_char_predicate_t
437 {
438 RESTINIO_NODISCARD
439 static constexpr bool
is_token_charrestinio::http_field_parsers::impl::is_token_char_predicate_t440 is_token_char( const char ch ) noexcept
441 {
442 return is_alpha(ch) || is_digit(ch) ||
443 ch == '!' ||
444 ch == '#' ||
445 ch == '$' ||
446 ch == '%' ||
447 ch == '&' ||
448 ch == '\'' ||
449 ch == '*' ||
450 ch == '+' ||
451 ch == '-' ||
452 ch == '.' ||
453 ch == '^' ||
454 ch == '_' ||
455 ch == '`' ||
456 ch == '|' ||
457 ch == '~';
458 }
459
460 RESTINIO_NODISCARD
461 bool
operator ()restinio::http_field_parsers::impl::is_token_char_predicate_t462 operator()( const char actual ) const noexcept
463 {
464 return is_token_char(actual);
465 }
466 };
467
468 //
469 // ows_producer_t
470 //
471 /*!
472 * @brief A producer for OWS.
473 *
474 * If an OWS found in the input stream it produces non-empty
475 * optional_t<char> with SP as the value.
476 *
477 * See: https://tools.ietf.org/html/rfc7230
478 *
479 * @since v.0.6.1
480 */
481 class ows_producer_t : public producer_tag< restinio::optional_t<char> >
482 {
483 public :
484 RESTINIO_NODISCARD
485 expected_t< result_type, parse_error_t >
try_parse(source_t & from) const486 try_parse(
487 source_t & from ) const noexcept
488 {
489 std::size_t extracted_spaces{};
490 character_t ch;
491 for( ch = from.getch();
492 !ch.m_eof && is_space(ch.m_ch);
493 ch = from.getch() )
494 {
495 ++extracted_spaces;
496 }
497
498 if( !ch.m_eof )
499 // The first non-space char should be returned back.
500 from.putback();
501
502 if( extracted_spaces > 0u )
503 return result_type{ ' ' };
504
505 return result_type{ nullopt };
506 }
507 };
508
509 //
510 // token_producer_t
511 //
512 /*!
513 * @brief A producer for token.
514 *
515 * If a token is found in the input stream it produces std::string
516 * with the value of that token.
517 *
518 * See: https://tools.ietf.org/html/rfc7230
519 *
520 * @since v.0.6.1
521 */
522 class token_producer_t : public producer_tag< std::string >
523 {
524 RESTINIO_NODISCARD
525 static optional_t< parse_error_t >
try_parse_value(source_t & from,std::string & accumulator)526 try_parse_value( source_t & from, std::string & accumulator )
527 {
528 error_reason_t reason = error_reason_t::pattern_not_found;
529
530 do
531 {
532 const auto ch = from.getch();
533 if( ch.m_eof )
534 {
535 reason = error_reason_t::unexpected_eof;
536 break;
537 }
538
539 if( !is_token_char(ch.m_ch) )
540 {
541 from.putback();
542 reason = error_reason_t::unexpected_character;
543 break;
544 }
545
546 accumulator += ch.m_ch;
547 }
548 while( true );
549
550 if( accumulator.empty() )
551 {
552 return parse_error_t{ from.current_position(), reason };
553 }
554
555 return nullopt;
556 }
557
558 RESTINIO_NODISCARD
559 static constexpr bool
is_token_char(const char ch)560 is_token_char( const char ch ) noexcept
561 {
562 return is_token_char_predicate_t::is_token_char( ch );
563 }
564
565 public :
566 RESTINIO_NODISCARD
567 expected_t< result_type, parse_error_t >
try_parse(source_t & from) const568 try_parse( source_t & from ) const
569 {
570 std::string value;
571 const auto try_result = try_parse_value( from, value );
572 if( !try_result )
573 return { std::move(value) };
574 else
575 return make_unexpected( *try_result );
576 }
577 };
578
579 //
580 // quoted_string_producer_t
581 //
582 /*!
583 * @brief A producer for quoted_string.
584 *
585 * If a quoted_string is found in the input stream it produces std::string
586 * with the value of that token.
587 *
588 * See: https://tools.ietf.org/html/rfc7230
589 *
590 * @since v.0.6.1
591 */
592 class quoted_string_producer_t : public producer_tag< std::string >
593 {
594 RESTINIO_NODISCARD
595 static optional_t< parse_error_t >
try_parse_value(source_t & from,std::string & accumulator)596 try_parse_value( source_t & from, std::string & accumulator )
597 {
598 error_reason_t reason = error_reason_t::pattern_not_found;
599
600 bool second_quote_extracted{ false };
601 do
602 {
603 const auto ch = from.getch();
604 if( ch.m_eof )
605 {
606 reason = error_reason_t::unexpected_eof;
607 break;
608 }
609
610 if( '"' == ch.m_ch )
611 second_quote_extracted = true;
612 else if( '\\' == ch.m_ch )
613 {
614 const auto next = from.getch();
615 if( next.m_eof )
616 {
617 reason = error_reason_t::unexpected_eof;
618 break;
619 }
620 else if( SP == next.m_ch || HTAB == next.m_ch ||
621 is_vchar( next.m_ch ) ||
622 is_obs_text( next.m_ch ) )
623 {
624 accumulator += next.m_ch;
625 }
626 else
627 {
628 reason = error_reason_t::unexpected_character;
629 from.putback();
630 break;
631 }
632 }
633 else if( is_qdtext( ch.m_ch ) )
634 accumulator += ch.m_ch;
635 else
636 {
637 reason = error_reason_t::unexpected_character;
638 from.putback();
639 break;
640 }
641 }
642 while( !second_quote_extracted );
643
644 if( !second_quote_extracted )
645 return parse_error_t{ from.current_position(), reason };
646 else
647 return nullopt;
648 }
649
650 public :
651 RESTINIO_NODISCARD
652 expected_t< result_type, parse_error_t >
try_parse(source_t & from) const653 try_parse( source_t & from ) const
654 {
655 source_t::content_consumer_t consumer{ from };
656
657 const auto ch = from.getch();
658 if( !ch.m_eof )
659 {
660 if( '"' == ch.m_ch )
661 {
662 std::string value;
663 const auto try_result = try_parse_value( from, value );
664 if( !try_result )
665 {
666 consumer.commit();
667 return { std::move(value) };
668 }
669 else
670 return make_unexpected( *try_result );
671 }
672 else
673 {
674 return make_unexpected( parse_error_t{
675 consumer.started_at(),
676 error_reason_t::unexpected_character
677 } );
678 }
679 }
680 else
681 return make_unexpected( parse_error_t{
682 consumer.started_at(),
683 error_reason_t::unexpected_eof
684 } );
685 }
686 };
687
688 //
689 // quoted_pair_producer_t
690 //
691 /*!
692 * @brief A producer for quoted_pair.
693 *
694 * If a quoted_pair is found in the input stream it produces char
695 * with the value of that token.
696 *
697 * See: https://tools.ietf.org/html/rfc7230
698 *
699 * @since v.0.6.4
700 */
701 class quoted_pair_producer_t : public producer_tag< char >
702 {
703 public :
704 RESTINIO_NODISCARD
705 expected_t< result_type, parse_error_t >
try_parse(source_t & from) const706 try_parse( source_t & from ) const
707 {
708 source_t::content_consumer_t consumer{ from };
709
710 error_reason_t reason = error_reason_t::unexpected_eof;
711
712 const auto ch = from.getch();
713 if( !ch.m_eof )
714 {
715 if( '\\' == ch.m_ch )
716 {
717 const auto next = from.getch();
718 if( !next.m_eof )
719 {
720 if( SP == next.m_ch || HTAB == next.m_ch ||
721 is_vchar( next.m_ch ) ||
722 is_obs_text( next.m_ch ) )
723 {
724 consumer.commit();
725 return next.m_ch;
726 }
727
728 reason = error_reason_t::unexpected_character;
729 }
730 }
731 else
732 reason = error_reason_t::unexpected_character;
733 }
734
735 return make_unexpected( parse_error_t{
736 from.current_position(),
737 reason
738 } );
739 }
740 };
741
742 //
743 // comment_producer_t
744 //
745 /*!
746 * @brief A producer for comment.
747 *
748 * If a comment is found in the input stream it produces std::string
749 * with the value of that token.
750 *
751 * Comment is defined that way:
752 @verbatim
753 comment = "(" *( ctext / quoted-pair / comment ) ")"
754 ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
755 quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
756 @endverbatim
757 *
758 * See: https://tools.ietf.org/html/rfc7230
759 *
760 * @since v.0.6.4
761 */
762 class comment_producer_t : public producer_tag< std::string >
763 {
764 public :
765 RESTINIO_NODISCARD
766 expected_t< result_type, parse_error_t >
767 try_parse( source_t & from ) const; // NOTE: implementation below.
768 };
769
770 } /* namespace impl */
771
772 //
773 // alpha_symbol_producer
774 //
775 /*!
776 * @brief A factory for producer of ALPHA symbols.
777 *
778 * Usage example:
779 * @code
780 produce<std::string>(
781 repeat(1, 20, alpha_symbol_p() >> to_container());
782 * @endcode
783 *
784 * @since v.0.6.2
785 */
786 RESTINIO_NODISCARD
787 inline auto
alpha_symbol_p()788 alpha_symbol_p()
789 {
790 return restinio::easy_parser::impl::symbol_producer_template_t<
791 impl::is_alpha_predicate_t >{};
792 }
793
794 //
795 // alphanum_symbol_producer
796 //
797 /*!
798 * @brief A factory for producer of symbol that an ALPHA or DIGIT.
799 *
800 * Usage example:
801 * @code
802 produce<std::string>(
803 repeat(1, 20, alphanum_symbol_p() >> to_container());
804 * @endcode
805 *
806 * @since v.0.6.2
807 */
808 RESTINIO_NODISCARD
809 inline auto
alphanum_symbol_p()810 alphanum_symbol_p()
811 {
812 return restinio::easy_parser::impl::symbol_producer_template_t<
813 impl::is_alphanum_predicate_t >{};
814 }
815
816 //
817 // vchar_symbol_producer
818 //
819 /*!
820 * @brief A factory for producer of VCHAR symbols.
821 *
822 * Usage example:
823 * @code
824 produce<std::string>(
825 repeat(1, 20, vchar_symbol_p() >> to_container());
826 * @endcode
827 *
828 * @since v.0.6.2
829 */
830 RESTINIO_NODISCARD
831 inline auto
vchar_symbol_p()832 vchar_symbol_p()
833 {
834 return restinio::easy_parser::impl::symbol_producer_template_t<
835 impl::is_vchar_predicate_t >{};
836 }
837
838 //
839 // ctext_symbol_producer
840 //
841 /*!
842 * @brief A factory for producer of ctext symbols.
843 *
844 * Usage example:
845 * @code
846 produce<std::string>(
847 repeat(1, 20, ctext_symbol_p() >> to_container());
848 * @endcode
849 *
850 * @since v.0.6.4
851 */
852 RESTINIO_NODISCARD
853 inline auto
ctext_symbol_p()854 ctext_symbol_p()
855 {
856 return restinio::easy_parser::impl::symbol_producer_template_t<
857 impl::is_ctext_predicate_t >{};
858 }
859
860 //
861 // comment_producer
862 //
863 /*!
864 * @brief A factory for producer of comment token.
865 *
866 * Usage example:
867 * @code
868 produce<std::string>(
869 alternatives(
870 token_p() >> as_result(),
871 comment_p() >> as_result()
872 )
873 );
874 * @endcode
875 *
876 * @since v.0.6.4
877 */
878 RESTINIO_NODISCARD
879 inline auto
comment_p()880 comment_p()
881 {
882 return impl::comment_producer_t{};
883 }
884
885 //
886 // ows_producer
887 //
888 /*!
889 * @brief A factory function to create an ows_producer.
890 *
891 * Usage example:
892 * @code
893 * produce<std::string>(
894 * ows_p() >> skip(),
895 * symbol('v'),
896 * symbol('='),
897 * ows_p() >> skip(),
898 * token_p() >> as_result()
899 * );
900 * @endcode
901 *
902 * @note
903 * Factory function ows() can be used instead of expression `ows_p() >> skip()`.
904 *
905 * @since v.0.6.1
906 */
907 RESTINIO_NODISCARD
908 inline auto
ows_p()909 ows_p() noexcept { return impl::ows_producer_t{}; }
910
911 //
912 // ows
913 //
914 /*!
915 * @brief A factory function to create an OWS clause.
916 *
917 * This clause handles an optional sequence of spaces in the input stream and
918 * skips the value of that sequence.
919 *
920 * Usage example:
921 * @code
922 * produce<std::string>(
923 * ows(),
924 * symbol('v'),
925 * symbol('='),
926 * ows(),
927 * token_p() >> as_result()
928 * );
929 * @endcode
930 * This expression corresponds the following rule:
931 @verbatim
932 T := OWS 'v' '=' OWS token
933 @endverbatim
934 *
935 * @since v.0.6.1
936 */
937 RESTINIO_NODISCARD
938 inline auto
ows()939 ows() noexcept { return ows_p() >> skip(); }
940
941 //
942 // token_symbol_producer
943 //
944 /*!
945 * @brief A factory for producer of symbols than can be used in tokens.
946 *
947 * Usage example:
948 * @code
949 produce<std::string>(
950 repeat(1, 20, token_symbol_p() >> to_container());
951 * @endcode
952 *
953 * @since v.0.6.9
954 */
955 RESTINIO_NODISCARD
956 inline auto
token_symbol_p()957 token_symbol_p() noexcept
958 {
959 return restinio::easy_parser::impl::symbol_producer_template_t<
960 impl::is_token_char_predicate_t >{};
961 }
962
963 //
964 // token_producer
965 //
966 /*!
967 * @brief A factory function to create a token_producer.
968 *
969 * Usage example:
970 * @code
971 * using parameter = std::pair<std::string, std::string>;
972 * produce<parameter>(
973 * ows(),
974 * token_p() >> ¶meter::first,
975 * symbol('='),
976 * ows(),
977 * token_p() >> ¶meter::second
978 * );
979 * @endcode
980 *
981 * @since v.0.6.1
982 */
983 RESTINIO_NODISCARD
984 inline auto
token_p()985 token_p() noexcept { return impl::token_producer_t{}; }
986
987 //
988 // quoted_string_producer
989 //
990 /*!
991 * @brief A factory function to create a quoted_string_producer.
992 *
993 * Usage example:
994 * @code
995 * using parameter = std::pair<std::string, std::string>;
996 * produce<parameter>(
997 * ows(),
998 * token_p() >> ¶meter::first,
999 * symbol('='),
1000 * ows(),
1001 * alternatives(
1002 * token_p() >> ¶meter::second,
1003 * quoted_string_p() >> ¶meter::second
1004 * )
1005 * );
1006 * @endcode
1007 *
1008 * @since v.0.6.1
1009 */
1010 RESTINIO_NODISCARD
1011 inline auto
quoted_string_p()1012 quoted_string_p() noexcept
1013 {
1014 return impl::quoted_string_producer_t{};
1015 }
1016
1017 //
1018 // quoted_pair_producer
1019 //
1020 /*!
1021 * @brief A factory function to create a quoted_pair_producer.
1022 *
1023 * Usage example:
1024 * @code
1025 * produce<std::string>(
1026 * repeat(1, N,
1027 * alternatives(
1028 * ctext_symbol_p() >> to_container(),
1029 * quoted_pair_p() >> to_container()
1030 * )
1031 * );
1032 * @endcode
1033 *
1034 * @since v.0.6.4
1035 */
1036 RESTINIO_NODISCARD
1037 inline auto
quoted_pair_p()1038 quoted_pair_p() noexcept
1039 {
1040 return impl::quoted_pair_producer_t{};
1041 }
1042
1043 //
1044 // expected_token_p
1045 //
1046 /*!
1047 * @brief A factory function to create a producer that expect a
1048 * token with specific value.
1049 *
1050 * If the expected token is successfully parsed then boolean value
1051 * is produced.
1052 *
1053 * Usage example:
1054 * @code
1055 * enum class compression { zlib, bz2, xz };
1056 * produce<compression>(
1057 * expected_token_p("compression") >> skip(),
1058 * ows(),
1059 * symbol('='),
1060 * ows(),
1061 * alternatives(
1062 * expected_token_p("zlib") >> just_result(compression::zlib),
1063 * expected_token_p("bz2") >> just_result(compression::bz2),
1064 * expected_token_p("xz") >> just_result(compression::xz)
1065 * )
1066 * );
1067 * @endcode
1068 *
1069 * @since v.0.6.9
1070 */
1071 RESTINIO_NODISCARD
1072 inline auto
expected_token_p(string_view_t token)1073 expected_token_p( string_view_t token )
1074 {
1075 return produce< bool >(
1076 exact_p( token ) >> as_result(),
1077 not_clause( token_symbol_p() >> skip() ) );
1078 }
1079
1080 //
1081 // expected_caseless_token_p
1082 //
1083 /*!
1084 * @brief A factory function to create a producer that expect a
1085 * token with specific value.
1086 *
1087 * This processer uses case-insensitive comparison.
1088 *
1089 * If the expected token is successfully parsed then boolean value
1090 * is produced.
1091 *
1092 * Usage example:
1093 * @code
1094 * enum class compression { zlib, bz2, xz };
1095 * produce<compression>(
1096 * caseless_expected_token_p("Compression") >> skip(),
1097 * ows(),
1098 * symbol('='),
1099 * ows(),
1100 * alternatives(
1101 * caseless_expected_token_p("ZLib") >> just_result(compression::zlib),
1102 * caseless_expected_token_p("BZ2") >> just_result(compression::bz2),
1103 * caseless_expected_token_p("xz") >> just_result(compression::xz)
1104 * )
1105 * );
1106 * @endcode
1107 *
1108 * @since v.0.6.9
1109 */
1110 RESTINIO_NODISCARD
1111 inline auto
expected_caseless_token_p(string_view_t token)1112 expected_caseless_token_p( string_view_t token )
1113 {
1114 return produce< bool >(
1115 caseless_exact_p( token ) >> as_result(),
1116 not_clause( token_symbol_p() >> skip() ) );
1117 }
1118
1119 namespace impl
1120 {
1121
1122 //
1123 // comment_producer_t implementation
1124 //
1125 RESTINIO_NODISCARD
1126 inline expected_t< comment_producer_t::result_type, parse_error_t >
try_parse(source_t & from) const1127 comment_producer_t::try_parse( source_t & from ) const
1128 {
1129 return produce< std::string >(
1130 sequence(
1131 symbol('('),
1132 repeat(0, N,
1133 alternatives(
1134 ctext_symbol_p() >> to_container(),
1135 quoted_pair_p() >> to_container(),
1136 comment_p() >> custom_consumer(
1137 []( std::string & dest, std::string && what ) {
1138 dest += what;
1139 } )
1140 ) ),
1141 symbol(')') )
1142 ).try_parse( from );
1143 }
1144
1145 //
1146 // qvalue_producer_t
1147 //
1148 /*!
1149 * @brief An implementation of producer of qvalue.
1150 *
1151 * Handles the following rule:
1152 @verbatim
1153 qvalue = ( "0" [ "." 0*3DIGIT ] )
1154 / ( "1" [ "." 0*3("0") ] )
1155 @endverbatim
1156 * and produces an instance of qvalue_t.
1157 *
1158 * See: https://tools.ietf.org/html/rfc7231
1159 *
1160 * @since v.0.6.1
1161 */
1162 class qvalue_producer_t
1163 : public producer_tag< qvalue_t >
1164 {
1165 // This type has to be used as type parameter for produce().
1166 struct zero_initialized_unit_t
1167 {
1168 qvalue_t::underlying_uint_t m_value{0u};
1169 };
1170
1171 //! A helper class to be used to accumulate actual integer
1172 //! while when the next digit is extracted from the input stream.
1173 class digit_consumer_t : public consumer_tag
1174 {
1175 const qvalue_t::underlying_uint_t m_multiplier;
1176
1177 public :
digit_consumer_t(qvalue_t::underlying_uint_t m)1178 constexpr digit_consumer_t( qvalue_t::underlying_uint_t m )
1179 : m_multiplier{ m }
1180 {}
1181
1182 void
consume(zero_initialized_unit_t & dest,char && digit)1183 consume( zero_initialized_unit_t & dest, char && digit )
1184 {
1185 dest.m_value += m_multiplier *
1186 static_cast< qvalue_t::underlying_uint_t >(digit - '0');
1187 }
1188 };
1189
1190 public :
1191 RESTINIO_NODISCARD
1192 expected_t< result_type, parse_error_t >
try_parse(source_t & from) const1193 try_parse( source_t & from ) const noexcept
1194 {
1195 const auto parse_result = produce< zero_initialized_unit_t >(
1196 alternatives(
1197 sequence(
1198 symbol('0'),
1199 maybe(
1200 symbol('.'),
1201 maybe( digit_p() >> digit_consumer_t{100},
1202 maybe( digit_p() >> digit_consumer_t{10},
1203 maybe( digit_p() >> digit_consumer_t{1} )
1204 )
1205 )
1206 )
1207 ),
1208 sequence(
1209 symbol_p('1') >> digit_consumer_t{1000},
1210 maybe(
1211 symbol('.'),
1212 maybe( symbol('0'),
1213 maybe( symbol('0'),
1214 maybe( symbol('0') )
1215 )
1216 )
1217 )
1218 )
1219 )
1220 ).try_parse( from );
1221
1222 if( parse_result )
1223 return qvalue_t{ qvalue_t::trusted{ parse_result->m_value } };
1224 else
1225 return make_unexpected( parse_result.error() );
1226 }
1227 };
1228
1229 } /* namespace impl */
1230
1231 //
1232 // qvalue_producer
1233 //
1234 /*!
1235 * @brief A factory function to create a qvalue_producer.
1236 *
1237 * Usage example:
1238 * @code
1239 * produce<qvalue_t>(
1240 * alternatives(symbol('r'), symbol('R')),
1241 * symbol('='),
1242 * qvalue_p() >> as_result()
1243 * );
1244 * @endcode
1245 *
1246 * @since v.0.6.1
1247 */
1248 RESTINIO_NODISCARD
1249 inline auto
qvalue_p()1250 qvalue_p() noexcept
1251 {
1252 return impl::qvalue_producer_t{};
1253 }
1254
1255 //
1256 // weight_producer
1257 //
1258 /*!
1259 * @brief A factory function to create a producer for weight parameter.
1260 *
1261 * Returns a producer that handles the following rules:
1262 @verbatim
1263 weight = OWS ';' OWS ('q' / 'Q') '=' qvalue
1264
1265 qvalue = ( "0" [ "." 0*3DIGIT ] )
1266 / ( "1" [ "." 0*3("0") ] )
1267 @endverbatim
1268 *
1269 * See: https://tools.ietf.org/html/rfc7231
1270 *
1271 * That producer produces a value of type qvalue_t.
1272 *
1273 * @since v.0.6.1
1274 */
1275 RESTINIO_NODISCARD
1276 inline auto
weight_p()1277 weight_p() noexcept
1278 {
1279 return produce< qvalue_t >(
1280 ows(),
1281 symbol(';'),
1282 ows(),
1283 caseless_symbol('q'),
1284 symbol('='),
1285 qvalue_p() >> as_result()
1286 );
1287 }
1288
1289 namespace impl
1290 {
1291
1292 //
1293 // non_empty_comma_separated_list_producer_t
1294 //
1295 /*!
1296 * @brief A template for a producer that handles non-empty list of
1297 * comma-separated values.
1298 *
1299 * That producer handles the following rule:
1300 @verbatim
1301 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] )
1302 @endverbatim
1303 *
1304 * See: https://tools.ietf.org/html/rfc7230
1305 * (section "7. ABNF List Extension: #rule")
1306 *
1307 * @tparam Container the type of container to be produced.
1308 *
1309 * @tparam Element_Producer the type of the producer of a single item.
1310 *
1311 * @since v.0.6.1
1312 */
1313 template<
1314 typename Container,
1315 typename Element_Producer >
1316 class non_empty_comma_separated_list_producer_t
1317 : public producer_tag< Container >
1318 {
1319 static_assert( impl::is_producer_v<Element_Producer>,
1320 "Element_Producer should be a value producer type" );
1321
1322 Element_Producer m_element;
1323
1324 public :
1325 using typename producer_tag< Container >::result_type;
1326
non_empty_comma_separated_list_producer_t(Element_Producer && element)1327 non_empty_comma_separated_list_producer_t(
1328 Element_Producer && element )
1329 : m_element{ std::move(element) }
1330 {}
1331
1332 RESTINIO_NODISCARD
1333 expected_t< result_type, parse_error_t >
try_parse(source_t & from)1334 try_parse( source_t & from )
1335 {
1336 Container tmp_value;
1337
1338 const auto appender = to_container();
1339
1340 const auto process_result = sequence(
1341 repeat( 0, N, symbol(','), ows() ),
1342 m_element >> appender,
1343 repeat( 0, N,
1344 ows(), symbol(','),
1345 maybe( ows(), m_element >> appender )
1346 )
1347 ).try_process( from, tmp_value );
1348
1349 if( !process_result )
1350 return { std::move(tmp_value) };
1351 else
1352 return make_unexpected( *process_result );
1353 }
1354 };
1355
1356 //
1357 // maybe_empty_comma_separated_list_producer_t
1358 //
1359 /*!
1360 * @brief A template for a producer that handles possibly empty list of
1361 * comma-separated values.
1362 *
1363 * That producer handles the following rule:
1364 @verbatim
1365 #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ]
1366 @endverbatim
1367 *
1368 * See: https://tools.ietf.org/html/rfc7230
1369 * (section "7. ABNF List Extension: #rule")
1370 *
1371 * @tparam Container the type of container to be produced.
1372 *
1373 * @tparam Element_Producer the type of the producer of a single item.
1374 *
1375 * @since v.0.6.1
1376 */
1377 template<
1378 typename Container,
1379 typename Element_Producer >
1380 class maybe_empty_comma_separated_list_producer_t
1381 : public producer_tag< Container >
1382 {
1383 static_assert( impl::is_producer_v<Element_Producer>,
1384 "Element_Producer should be a value producer type" );
1385
1386 Element_Producer m_element;
1387
1388 public :
1389 using typename producer_tag< Container >::result_type;
1390
maybe_empty_comma_separated_list_producer_t(Element_Producer && element)1391 maybe_empty_comma_separated_list_producer_t(
1392 Element_Producer && element )
1393 : m_element{ std::move(element) }
1394 {}
1395
1396 RESTINIO_NODISCARD
1397 expected_t< result_type, parse_error_t >
try_parse(source_t & from)1398 try_parse( source_t & from )
1399 {
1400 Container tmp_value;
1401
1402 const auto appender = to_container();
1403
1404 const auto process_result = maybe(
1405 alternatives( symbol(','), m_element >> appender ),
1406 repeat( 0, N,
1407 ows(), symbol(','),
1408 maybe( ows(), m_element >> appender )
1409 )
1410 ).try_process( from, tmp_value );
1411
1412 if( !process_result )
1413 return { std::move(tmp_value) };
1414 else
1415 return make_unexpected( *process_result );
1416 }
1417 };
1418
1419 } /* namespace impl */
1420
1421 //
1422 // non_empty_comma_separated_list_producer
1423 //
1424 /*!
1425 * @brief A factory for a producer that handles non-empty list of
1426 * comma-separated values.
1427 *
1428 * That producer handles the following rule:
1429 @verbatim
1430 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] )
1431 @endverbatim
1432 *
1433 * See: https://tools.ietf.org/html/rfc7230
1434 * (section "7. ABNF List Extension: #rule")
1435 *
1436 * Usage example:
1437 * @code
1438 auto parse = produce< byte_ranges_data >(
1439 make_bytes_prefix_parser(),
1440 non_empty_comma_separated_list_p< std::vector< byte_range > >(
1441 make_byte_range_parser()
1442 ) >> &byte_ranges_data::ranges
1443 );
1444 * @endcode
1445 *
1446 * @tparam Container the type of container to be produced.
1447 *
1448 * @tparam Element_Producer the type of the producer of a single item.
1449 *
1450 * @since v.0.6.1
1451 */
1452 template<
1453 typename Container,
1454 typename Element_Producer >
1455 RESTINIO_NODISCARD
1456 auto
non_empty_comma_separated_list_p(Element_Producer element)1457 non_empty_comma_separated_list_p( Element_Producer element )
1458 {
1459 static_assert( impl::is_producer_v<Element_Producer>,
1460 "Element_Producer should be a value producer type" );
1461
1462 return impl::non_empty_comma_separated_list_producer_t<
1463 Container,
1464 Element_Producer >{ std::move(element) };
1465 }
1466
1467 //
1468 // maybe_empty_comma_separated_list_producer
1469 //
1470 /*!
1471 * @brief A factory for a producer that handles possibly empty list of
1472 * comma-separated values.
1473 *
1474 * That producer handles the following rule:
1475 @verbatim
1476 #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ]
1477 @endverbatim
1478 *
1479 * See: https://tools.ietf.org/html/rfc7230
1480 * (section "7. ABNF List Extension: #rule")
1481 *
1482 * Usage example:
1483 * @code
1484 auto parse = produce< byte_ranges_data >(
1485 make_bytes_prefix_parser(),
1486 maybe_empty_comma_separated_list_p< std::vector< byte_range > >(
1487 make_byte_range_parser()
1488 ) >> &byte_ranges_data::ranges
1489 );
1490 * @endcode
1491 *
1492 * @tparam Container the type of container to be produced.
1493 *
1494 * @tparam Element_Producer the type of the producer of a single item.
1495 *
1496 * @since v.0.6.1
1497 */
1498 template<
1499 typename Container,
1500 typename Element_Producer >
1501 RESTINIO_NODISCARD
1502 auto
maybe_empty_comma_separated_list_p(Element_Producer element)1503 maybe_empty_comma_separated_list_p( Element_Producer element )
1504 {
1505 static_assert( impl::is_producer_v<Element_Producer>,
1506 "Element_Producer should be a value producer type" );
1507
1508 return impl::maybe_empty_comma_separated_list_producer_t<
1509 Container,
1510 Element_Producer >{ std::move(element) };
1511 }
1512
1513 //
1514 // parameter_with_mandatory_value_t
1515 //
1516 /*!
1517 * @brief A type that describes a parameter with mandatory value.
1518 *
1519 * @since v.0.6.1
1520 */
1521 using parameter_with_mandatory_value_t = std::pair< std::string, std::string >;
1522
1523 //
1524 // parameter_with_mandatory_value_container_t
1525 //
1526 /*!
1527 * @brief A type of container for parameters with mandatory values.
1528 *
1529 * @since v.0.6.1
1530 */
1531 using parameter_with_mandatory_value_container_t =
1532 std::vector< parameter_with_mandatory_value_t >;
1533
1534 //
1535 // not_found_t
1536 //
1537 /*!
1538 * @brief An empty type to be used as indicator of negative search result.
1539 *
1540 * @since v.0.6.1
1541 */
1542 struct not_found_t {};
1543
1544 //
1545 // find_first
1546 //
1547 /*!
1548 * @brief A helper function to find the first occurence of a parameter
1549 * with the specified value.
1550 *
1551 * @note
1552 * The caseless (case-insentive) search is used. It means that
1553 * search with value "charset" will returns items "CharSet", "charset",
1554 * "CHARSET" and so on.
1555 *
1556 * Usage example:
1557 * @code
1558 * const auto charset = find_first(content_type_params, "charset");
1559 * if(charset) {
1560 * ... // Handle the value of charset parameter.
1561 * }
1562 * @endcode
1563 *
1564 * @since v.0.6.1
1565 */
1566 RESTINIO_NODISCARD
1567 inline expected_t< string_view_t, not_found_t >
find_first(const parameter_with_mandatory_value_container_t & where,string_view_t what)1568 find_first(
1569 const parameter_with_mandatory_value_container_t & where,
1570 string_view_t what )
1571 {
1572 const auto it = std::find_if( where.begin(), where.end(),
1573 [&what]( const auto & pair ) {
1574 return restinio::impl::is_equal_caseless( pair.first, what );
1575 } );
1576 if( it != where.end() )
1577 return string_view_t{ it->second };
1578 else
1579 return make_unexpected( not_found_t{} );
1580 }
1581
1582 namespace impl
1583 {
1584
1585 namespace params_with_value_producer_details
1586 {
1587
1588 //
1589 // make_parser
1590 //
1591 /*!
1592 * @brief Helper function that creates an instance of producer
1593 * of parameter_with_mandatory_value_container.
1594 *
1595 * @since v.0.6.1
1596 */
1597 RESTINIO_NODISCARD
1598 inline auto
make_parser()1599 make_parser()
1600 {
1601 return produce< parameter_with_mandatory_value_container_t >(
1602 repeat( 0, N,
1603 produce< parameter_with_mandatory_value_t >(
1604 ows(),
1605 symbol(';'),
1606 ows(),
1607 token_p() >> to_lower()
1608 >> ¶meter_with_mandatory_value_t::first,
1609 symbol('='),
1610 alternatives(
1611 token_p()
1612 >> ¶meter_with_mandatory_value_t::second,
1613 quoted_string_p()
1614 >> ¶meter_with_mandatory_value_t::second
1615 )
1616 ) >> to_container()
1617 )
1618 );
1619 }
1620
1621 } /* namespace params_with_value_producer_details */
1622
1623 //
1624 // params_with_value_producer_t
1625 //
1626 /*!
1627 * @brief A type of producer that produces instances of
1628 * parameter_with_mandatory_value_container.
1629 *
1630 * @since v.0.6.1
1631 */
1632 class params_with_value_producer_t
1633 : public producer_tag< parameter_with_mandatory_value_container_t >
1634 {
1635 using actual_producer_t = std::decay_t<
1636 decltype(params_with_value_producer_details::make_parser()) >;
1637
1638 actual_producer_t m_producer{
1639 params_with_value_producer_details::make_parser() };
1640
1641 public :
1642 params_with_value_producer_t() = default;
1643
1644 RESTINIO_NODISCARD
1645 auto
try_parse(source_t & from)1646 try_parse( source_t & from )
1647 {
1648 return m_producer.try_parse( from );
1649 }
1650 };
1651
1652 } /* namespace impl */
1653
1654 //
1655 // params_with_value_producer
1656 //
1657 /*!
1658 * @brief A factory of producer of parameter_with_mandatory_value_container.
1659 *
1660 * Creates a produces that handles the following rule:
1661 @verbatim
1662 T := *( OWS ';' OWS token '=' OWS (token / quoted_string))
1663 @endverbatim
1664 *
1665 * Usage example:
1666 * @code
1667 * struct my_field {
1668 * std::string value;
1669 * parameter_with_mandatory_value_container_t params;
1670 * };
1671 * produce<my_field>(
1672 * token_p() >> to_lower() >> &my_field::value,
1673 * params_with_value_p() >> &my_field::params
1674 * );
1675 * @endcode
1676 *
1677 * @note
1678 * Parameters names are converted to lower case. Parameters' values
1679 * are not changed and stored as they are.
1680 *
1681 * @since v.0.6.1
1682 */
1683 RESTINIO_NODISCARD
1684 inline impl::params_with_value_producer_t
params_with_value_p()1685 params_with_value_p() { return {}; }
1686
1687 //
1688 // parameter_with_optional_value_t
1689 //
1690 /*!
1691 * @brief A type that describes a parameter with optional value.
1692 *
1693 * @since v.0.6.1
1694 */
1695 using parameter_with_optional_value_t =
1696 std::pair< std::string, restinio::optional_t<std::string> >;
1697
1698 //
1699 // parameter_with_optional_value_container_t
1700 //
1701 /*!
1702 * @brief A type of container for parameters with optional values.
1703 *
1704 * @since v.0.6.1
1705 */
1706 using parameter_with_optional_value_container_t =
1707 std::vector< parameter_with_optional_value_t >;
1708
1709 //
1710 // find_first
1711 //
1712 /*!
1713 * @brief A helper function to find the first occurence of a parameter
1714 * with the specified value.
1715 *
1716 * @note
1717 * The caseless (case-insentive) search is used. It means that
1718 * search with value "charset" will returns items "CharSet", "charset",
1719 * "CHARSET" and so on.
1720 *
1721 * Usage example:
1722 * @code
1723 * const auto max_age = find_first(cache_control_params, "max-age");
1724 * if(max_age) {
1725 * if(*max_age) {
1726 * ... // Handle the value of max-age parameter.
1727 * }
1728 * else {
1729 * ... // Handle the case where max-age specified but without a value.
1730 * }
1731 * }
1732 * @endcode
1733 *
1734 * @since v.0.6.1
1735 */
1736 RESTINIO_NODISCARD
1737 inline expected_t< restinio::optional_t<string_view_t>, not_found_t >
find_first(const parameter_with_optional_value_container_t & where,string_view_t what)1738 find_first(
1739 const parameter_with_optional_value_container_t & where,
1740 string_view_t what )
1741 {
1742 const auto it = std::find_if( where.begin(), where.end(),
1743 [&what]( const auto & pair ) {
1744 return restinio::impl::is_equal_caseless( pair.first, what );
1745 } );
1746 if( it != where.end() )
1747 {
1748 const auto opt = it->second;
1749 if( opt )
1750 return string_view_t{ *opt };
1751 else
1752 return restinio::optional_t< string_view_t >{ nullopt };
1753 }
1754 else
1755 return make_unexpected( not_found_t{} );
1756 }
1757
1758 namespace impl
1759 {
1760
1761 namespace params_with_opt_value_producer_details
1762 {
1763
1764 //
1765 // make_parser
1766 //
1767 /*!
1768 * @brief Helper function that creates an instance of producer
1769 * of parameter_with_optional_value_container.
1770 *
1771 * @since v.0.6.1
1772 */
1773 RESTINIO_NODISCARD
1774 inline auto
make_parser()1775 make_parser()
1776 {
1777 return produce< parameter_with_optional_value_container_t >(
1778 repeat( 0, N,
1779 produce< parameter_with_optional_value_t >(
1780 ows(),
1781 symbol(';'),
1782 ows(),
1783 token_p() >> to_lower()
1784 >> ¶meter_with_optional_value_t::first,
1785 maybe(
1786 symbol('='),
1787 alternatives(
1788 token_p()
1789 >> ¶meter_with_optional_value_t::second,
1790 quoted_string_p()
1791 >> ¶meter_with_optional_value_t::second
1792 )
1793 )
1794 ) >> to_container()
1795 )
1796 );
1797 }
1798
1799 } /* namespace params_with_opt_value_producer_details */
1800
1801 //
1802 // params_with_opt_value_producer_t
1803 //
1804 /*!
1805 * @brief A type of producer that produces instances of
1806 * parameter_with_optional_value_container.
1807 *
1808 * @since v.0.6.1
1809 */
1810 class params_with_opt_value_producer_t
1811 : public producer_tag< parameter_with_optional_value_container_t >
1812 {
1813 using actual_producer_t = std::decay_t<
1814 decltype(params_with_opt_value_producer_details::make_parser()) >;
1815
1816 actual_producer_t m_producer{
1817 params_with_opt_value_producer_details::make_parser() };
1818
1819 public :
1820 params_with_opt_value_producer_t() = default;
1821
1822 RESTINIO_NODISCARD
1823 auto
try_parse(source_t & from)1824 try_parse( source_t & from )
1825 {
1826 return m_producer.try_parse( from );
1827 }
1828 };
1829
1830 } /* namespace impl */
1831
1832 //
1833 // params_with_opt_value_producer
1834 //
1835 /*!
1836 * @brief A factory of producer of parameter_with_optional_value_container.
1837 *
1838 * Creates a produces that handles the following rule:
1839 @verbatim
1840 T := *( OWS ';' OWS token ['=' OWS (token / quoted_string)] )
1841 @endverbatim
1842 *
1843 * Usage example:
1844 * @code
1845 * struct my_field {
1846 * std::string value;
1847 * parameter_with_optional_value_container_t params;
1848 * };
1849 * produce<my_field>(
1850 * token_p() >> to_lower() >> &my_field::value,
1851 * params_with_opt_value_p() >> &my_field::params
1852 * );
1853 * @endcode
1854 *
1855 * @note
1856 * Parameters names are converted to lower case. Parameters' values
1857 * are not changed and stored as they are.
1858 *
1859 * @since v.0.6.1
1860 */
1861 RESTINIO_NODISCARD
1862 inline impl::params_with_opt_value_producer_t
params_with_opt_value_p()1863 params_with_opt_value_p() { return {}; }
1864
1865 } /* namespace http_field_parser */
1866
1867 } /* namespace restinio */
1868
1869