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() >> &parameter::first,
975  * 	symbol('='),
976  * 	ows(),
977  * 	token_p() >> &parameter::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() >> &parameter::first,
999  * 	symbol('='),
1000  * 	ows(),
1001  * 	alternatives(
1002  * 		token_p() >> &parameter::second,
1003  * 		quoted_string_p() >> &parameter::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 							>> &parameter_with_mandatory_value_t::first,
1609 					symbol('='),
1610 					alternatives(
1611 						token_p()
1612 								>> &parameter_with_mandatory_value_t::second,
1613 						quoted_string_p()
1614 								>> &parameter_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 							>> &parameter_with_optional_value_t::first,
1785 					maybe(
1786 						symbol('='),
1787 						alternatives(
1788 							token_p()
1789 									>> &parameter_with_optional_value_t::second,
1790 							quoted_string_p()
1791 									>> &parameter_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