1 /*!
2  * @file
3  * @brief Helpers for parsing IP-addresses.
4  */
5 
6 #pragma once
7 
8 #include <restinio/helpers/easy_parser.hpp>
9 
10 #include <asio/ip/address.hpp>
11 
12 namespace arataga::utils::parsers
13 {
14 
15 //
16 // is_ip_address_char_predicate_t
17 //
18 /*!
19  * @brief A predicate for easy_parser that detects symbols enabled
20  * to be used in IP-addresses.
21  */
22 struct is_ip_address_char_predicate_t
23 {
24 	[[nodiscard]]
25 	bool
operator ()arataga::utils::parsers::is_ip_address_char_predicate_t26 	operator()( char ch ) const noexcept
27 	{
28 		return restinio::easy_parser::impl::is_hexdigit(ch)
29 				|| '.' == ch
30 				|| ':' == ch
31 				;
32 	}
33 };
34 
35 //
36 // ip_address_char_p
37 //
38 /*!
39  * @brief A producer for easy_parser that extracts symbols enabled
40  * to be used in IP-addresses.
41  */
42 [[nodiscard]]
43 inline auto
ip_address_char_p()44 ip_address_char_p() noexcept
45 {
46 	return restinio::easy_parser::impl::symbol_producer_template_t<
47 			is_ip_address_char_predicate_t >{};
48 }
49 
50 //
51 // ip_address_char_seq_p
52 //
53 /*!
54  * @brief A producer for easy_parser that extracts a sequence of
55  * symbols enabled to be used in IP-addresses.
56  *
57  * Produces an instance of std::string.
58  */
59 [[nodiscard]]
60 inline auto
ip_address_char_seq_p()61 ip_address_char_seq_p() noexcept
62 {
63 	using namespace restinio::easy_parser;
64 
65 	return produce< std::string >(
66 			repeat( 1, N, ip_address_char_p() >> to_container() )
67 		);
68 }
69 
70 //
71 // ipv4_address_p
72 //
73 /*!
74  * @brief A procuder for easy_parser that extracts IPv4-address.
75  *
76  * Produces an instance of asio::ip::address_v4.
77  */
78 [[nodiscard]]
79 inline auto
ipv4_address_p()80 ipv4_address_p() noexcept
81 {
82 	using namespace restinio::easy_parser;
83 
84 	using byte_t = asio::ip::address_v4::bytes_type::value_type;
85 	const auto one_group = non_negative_decimal_number_p< byte_t >();
86 
87 	return produce< asio::ip::address_v4 >(
88 		produce< asio::ip::address_v4::bytes_type >(
89 			repeat( 3u, 3u, one_group >> to_container(), symbol('.') ),
90 			one_group >> to_container()
91 		)
92 		>> convert( []( const auto & arr ) {
93 				return asio::ip::make_address_v4( arr );
94 			} )
95 		>> as_result()
96 	);
97 }
98 
99 //
100 // ip_address_p
101 //
102 /*!
103  * @brief A producer for easy_parser that extracts IP-address regardless of
104  * its version.
105  *
106  * Produces an instance of asio::ip::address.
107  *
108  * Can handle IPv4 and IPv6 addresses.
109  */
110 [[nodiscard]]
111 inline auto
ip_address_p()112 ip_address_p() noexcept
113 {
114 	using namespace restinio::easy_parser;
115 
116 	const auto try_extract_ip_address =
117 		[]( const std::string & ip_as_string ) ->
118 			restinio::expected_t< asio::ip::address, error_reason_t >
119 		{
120 			asio::error_code ec;
121 			auto addr = asio::ip::make_address( ip_as_string, ec );
122 			if( ec )
123 				return restinio::make_unexpected(
124 						error_reason_t::illegal_value_found );
125 
126 			return { addr };
127 		};
128 
129 	return produce< asio::ip::address >(
130 			produce< std::string >(
131 				repeat( 1u, N, ip_address_char_p() >> to_container() )
132 			)
133 			>> convert( try_extract_ip_address )
134 			>> as_result()
135 		);
136 }
137 
138 } /* namespace arataga::utils::parsers */
139 
140