1  
//
1  
//
2  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
2  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/url
7  
// Official repository: https://github.com/boostorg/url
8  
//
8  
//
9  

9  

10  

10  

11  
#include <boost/url/detail/config.hpp>
11  
#include <boost/url/detail/config.hpp>
12  
#include "pattern.hpp"
12  
#include "pattern.hpp"
13  
#include "pct_format.hpp"
13  
#include "pct_format.hpp"
14  
#include "boost/url/detail/replacement_field_rule.hpp"
14  
#include "boost/url/detail/replacement_field_rule.hpp"
15  
#include <boost/url/grammar/alpha_chars.hpp>
15  
#include <boost/url/grammar/alpha_chars.hpp>
16  
#include <boost/url/grammar/optional_rule.hpp>
16  
#include <boost/url/grammar/optional_rule.hpp>
17  
#include <boost/url/grammar/token_rule.hpp>
17  
#include <boost/url/grammar/token_rule.hpp>
18  
#include "../rfc/detail/charsets.hpp"
18  
#include "../rfc/detail/charsets.hpp"
19  
#include "../rfc/detail/host_rule.hpp"
19  
#include "../rfc/detail/host_rule.hpp"
20  
#include "boost/url/rfc/detail/path_rules.hpp"
20  
#include "boost/url/rfc/detail/path_rules.hpp"
21  
#include "../rfc/detail/port_rule.hpp"
21  
#include "../rfc/detail/port_rule.hpp"
22  
#include "../rfc/detail/scheme_rule.hpp"
22  
#include "../rfc/detail/scheme_rule.hpp"
23  

23  

24  
namespace boost {
24  
namespace boost {
25  
namespace urls {
25  
namespace urls {
26  
namespace detail {
26  
namespace detail {
27  

27  

28  
static constexpr auto lhost_chars = host_chars + ':';
28  
static constexpr auto lhost_chars = host_chars + ':';
29  

29  

30  
void
30  
void
31  
pattern::
31  
pattern::
32  
apply(
32  
apply(
33  
    url_base& u,
33  
    url_base& u,
34  
    format_args const& args) const
34  
    format_args const& args) const
35  
{
35  
{
36  
    // measure total
36  
    // measure total
37  
    struct sizes
37  
    struct sizes
38  
    {
38  
    {
39  
        std::size_t scheme = 0;
39  
        std::size_t scheme = 0;
40  
        std::size_t user = 0;
40  
        std::size_t user = 0;
41  
        std::size_t pass = 0;
41  
        std::size_t pass = 0;
42  
        std::size_t host = 0;
42  
        std::size_t host = 0;
43  
        std::size_t port = 0;
43  
        std::size_t port = 0;
44  
        std::size_t path = 0;
44  
        std::size_t path = 0;
45  
        std::size_t query = 0;
45  
        std::size_t query = 0;
46  
        std::size_t frag = 0;
46  
        std::size_t frag = 0;
47  
    };
47  
    };
48  
    sizes n;
48  
    sizes n;
49  

49  

50  
    format_parse_context pctx(nullptr, nullptr, 0);
50  
    format_parse_context pctx(nullptr, nullptr, 0);
51  
    measure_context mctx(args);
51  
    measure_context mctx(args);
52  
    if (!scheme.empty())
52  
    if (!scheme.empty())
53  
    {
53  
    {
54  
        pctx = {scheme, pctx.next_arg_id()};
54  
        pctx = {scheme, pctx.next_arg_id()};
55  
        n.scheme = pct_vmeasure(
55  
        n.scheme = pct_vmeasure(
56  
            grammar::alpha_chars, pctx, mctx);
56  
            grammar::alpha_chars, pctx, mctx);
57  
        mctx.advance_to(0);
57  
        mctx.advance_to(0);
58  
    }
58  
    }
59  
    if (has_authority)
59  
    if (has_authority)
60  
    {
60  
    {
61  
        if (has_user)
61  
        if (has_user)
62  
        {
62  
        {
63  
            pctx = {user, pctx.next_arg_id()};
63  
            pctx = {user, pctx.next_arg_id()};
64  
            n.user = pct_vmeasure(
64  
            n.user = pct_vmeasure(
65  
                user_chars, pctx, mctx);
65  
                user_chars, pctx, mctx);
66  
            mctx.advance_to(0);
66  
            mctx.advance_to(0);
67  
            if (has_pass)
67  
            if (has_pass)
68  
            {
68  
            {
69  
                pctx = {pass, pctx.next_arg_id()};
69  
                pctx = {pass, pctx.next_arg_id()};
70  
                n.pass = pct_vmeasure(
70  
                n.pass = pct_vmeasure(
71  
                    password_chars, pctx, mctx);
71  
                    password_chars, pctx, mctx);
72  
                mctx.advance_to(0);
72  
                mctx.advance_to(0);
73  
            }
73  
            }
74  
        }
74  
        }
75  
        if (host.starts_with('['))
75  
        if (host.starts_with('['))
76  
        {
76  
        {
77  
            BOOST_ASSERT(host.ends_with(']'));
77  
            BOOST_ASSERT(host.ends_with(']'));
78  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
78  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
79  
            n.host = pct_vmeasure(
79  
            n.host = pct_vmeasure(
80  
                lhost_chars, pctx, mctx) + 2;
80  
                lhost_chars, pctx, mctx) + 2;
81  
            mctx.advance_to(0);
81  
            mctx.advance_to(0);
82  
        }
82  
        }
83  
        else
83  
        else
84  
        {
84  
        {
85  
            pctx = {host, pctx.next_arg_id()};
85  
            pctx = {host, pctx.next_arg_id()};
86  
            n.host = pct_vmeasure(
86  
            n.host = pct_vmeasure(
87  
                host_chars, pctx, mctx);
87  
                host_chars, pctx, mctx);
88  
            mctx.advance_to(0);
88  
            mctx.advance_to(0);
89  
        }
89  
        }
90  
        if (has_port)
90  
        if (has_port)
91  
        {
91  
        {
92  
            pctx = {port, pctx.next_arg_id()};
92  
            pctx = {port, pctx.next_arg_id()};
93  
            n.port = pct_vmeasure(
93  
            n.port = pct_vmeasure(
94  
                grammar::digit_chars, pctx, mctx);
94  
                grammar::digit_chars, pctx, mctx);
95  
            mctx.advance_to(0);
95  
            mctx.advance_to(0);
96  
        }
96  
        }
97  
    }
97  
    }
98  
    if (!path.empty())
98  
    if (!path.empty())
99  
    {
99  
    {
100  
        pctx = {path, pctx.next_arg_id()};
100  
        pctx = {path, pctx.next_arg_id()};
101  
        n.path = pct_vmeasure(
101  
        n.path = pct_vmeasure(
102  
            path_chars, pctx, mctx);
102  
            path_chars, pctx, mctx);
103  
        mctx.advance_to(0);
103  
        mctx.advance_to(0);
104  
    }
104  
    }
105  
    if (has_query)
105  
    if (has_query)
106  
    {
106  
    {
107  
        pctx = {query, pctx.next_arg_id()};
107  
        pctx = {query, pctx.next_arg_id()};
108  
        n.query = pct_vmeasure(
108  
        n.query = pct_vmeasure(
109  
            query_chars, pctx, mctx);
109  
            query_chars, pctx, mctx);
110  
        mctx.advance_to(0);
110  
        mctx.advance_to(0);
111  
    }
111  
    }
112  
    if (has_frag)
112  
    if (has_frag)
113  
    {
113  
    {
114  
        pctx = {frag, pctx.next_arg_id()};
114  
        pctx = {frag, pctx.next_arg_id()};
115  
        n.frag = pct_vmeasure(
115  
        n.frag = pct_vmeasure(
116  
            fragment_chars, pctx, mctx);
116  
            fragment_chars, pctx, mctx);
117  
        mctx.advance_to(0);
117  
        mctx.advance_to(0);
118  
    }
118  
    }
119  
    std::size_t const n_total =
119  
    std::size_t const n_total =
120  
        n.scheme +
120  
        n.scheme +
121  
        (n.scheme != 0) * 1 + // ":"
121  
        (n.scheme != 0) * 1 + // ":"
122  
        has_authority * 2 +   // "//"
122  
        has_authority * 2 +   // "//"
123  
        n.user +
123  
        n.user +
124  
        has_pass * 1 +        // ":"
124  
        has_pass * 1 +        // ":"
125  
        n.pass +
125  
        n.pass +
126  
        has_user * 1 +        // "@"
126  
        has_user * 1 +        // "@"
127  
        n.host +
127  
        n.host +
128  
        has_port * 1 +        // ":"
128  
        has_port * 1 +        // ":"
129  
        n.port +
129  
        n.port +
130  
        n.path +
130  
        n.path +
131  
        has_query * 1 +       // "?"
131  
        has_query * 1 +       // "?"
132  
        n.query +
132  
        n.query +
133  
        has_frag * 1 +        // "#"
133  
        has_frag * 1 +        // "#"
134  
        n.frag;
134  
        n.frag;
135  
    u.reserve(n_total);
135  
    u.reserve(n_total);
136  

136  

137  
    // Apply
137  
    // Apply
138  
    pctx = {nullptr, nullptr, 0};
138  
    pctx = {nullptr, nullptr, 0};
139  
    format_context fctx(nullptr, args);
139  
    format_context fctx(nullptr, args);
140  
    url_base::op_t op(u);
140  
    url_base::op_t op(u);
141  
    using parts = parts_base;
141  
    using parts = parts_base;
142  
    if (!scheme.empty())
142  
    if (!scheme.empty())
143  
    {
143  
    {
144  
        auto dest = u.resize_impl(
144  
        auto dest = u.resize_impl(
145  
            parts::id_scheme,
145  
            parts::id_scheme,
146  
            n.scheme + 1, op);
146  
            n.scheme + 1, op);
147  
        pctx = {scheme, pctx.next_arg_id()};
147  
        pctx = {scheme, pctx.next_arg_id()};
148  
        fctx.advance_to(dest);
148  
        fctx.advance_to(dest);
149  
        const char* dest1 = pct_vformat(
149  
        const char* dest1 = pct_vformat(
150  
            grammar::alpha_chars, pctx, fctx);
150  
            grammar::alpha_chars, pctx, fctx);
151  
        dest[n.scheme] = ':';
151  
        dest[n.scheme] = ':';
152  
        // validate
152  
        // validate
153  
        if (!grammar::parse({dest, dest1}, scheme_rule()))
153  
        if (!grammar::parse({dest, dest1}, scheme_rule()))
154  
        {
154  
        {
155  
            throw_invalid_argument();
155  
            throw_invalid_argument();
156  
        }
156  
        }
157  
    }
157  
    }
158  
    if (has_authority)
158  
    if (has_authority)
159  
    {
159  
    {
160  
        if (has_user)
160  
        if (has_user)
161  
        {
161  
        {
162  
            auto dest = u.set_user_impl(
162  
            auto dest = u.set_user_impl(
163  
                n.user, op);
163  
                n.user, op);
164  
            pctx = {user, pctx.next_arg_id()};
164  
            pctx = {user, pctx.next_arg_id()};
165  
            fctx.advance_to(dest);
165  
            fctx.advance_to(dest);
166  
            char const* dest1 = pct_vformat(
166  
            char const* dest1 = pct_vformat(
167  
                user_chars, pctx, fctx);
167  
                user_chars, pctx, fctx);
168  
            u.impl_.decoded_[parts::id_user] =
168  
            u.impl_.decoded_[parts::id_user] =
169  
                pct_string_view(dest, dest1 - dest)
169  
                pct_string_view(dest, dest1 - dest)
170  
                    ->decoded_size();
170  
                    ->decoded_size();
171  
            if (has_pass)
171  
            if (has_pass)
172  
            {
172  
            {
173  
                char* destp = u.set_password_impl(
173  
                char* destp = u.set_password_impl(
174  
                    n.pass, op);
174  
                    n.pass, op);
175  
                pctx = {pass, pctx.next_arg_id()};
175  
                pctx = {pass, pctx.next_arg_id()};
176  
                fctx.advance_to(destp);
176  
                fctx.advance_to(destp);
177  
                dest1 = pct_vformat(
177  
                dest1 = pct_vformat(
178  
                    password_chars, pctx, fctx);
178  
                    password_chars, pctx, fctx);
179  
                u.impl_.decoded_[parts::id_pass] =
179  
                u.impl_.decoded_[parts::id_pass] =
180  
                    pct_string_view({destp, dest1})
180  
                    pct_string_view({destp, dest1})
181  
                        ->decoded_size() + 1;
181  
                        ->decoded_size() + 1;
182  
            }
182  
            }
183  
        }
183  
        }
184  
        auto dest = u.set_host_impl(
184  
        auto dest = u.set_host_impl(
185  
            n.host, op);
185  
            n.host, op);
186  
        if (host.starts_with('['))
186  
        if (host.starts_with('['))
187  
        {
187  
        {
188  
            BOOST_ASSERT(host.ends_with(']'));
188  
            BOOST_ASSERT(host.ends_with(']'));
189  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
189  
            pctx = {host.substr(1, host.size() - 2), pctx.next_arg_id()};
190  
            *dest++ = '[';
190  
            *dest++ = '[';
191  
            fctx.advance_to(dest);
191  
            fctx.advance_to(dest);
192  
            char* dest1 =
192  
            char* dest1 =
193  
                pct_vformat(lhost_chars, pctx, fctx);
193  
                pct_vformat(lhost_chars, pctx, fctx);
194  
            *dest1++ = ']';
194  
            *dest1++ = ']';
195  
            u.impl_.decoded_[parts::id_host] =
195  
            u.impl_.decoded_[parts::id_host] =
196  
                pct_string_view(dest - 1, dest1 - dest)
196  
                pct_string_view(dest - 1, dest1 - dest)
197  
                    ->decoded_size();
197  
                    ->decoded_size();
198  
        }
198  
        }
199  
        else
199  
        else
200  
        {
200  
        {
201  
            pctx = {host, pctx.next_arg_id()};
201  
            pctx = {host, pctx.next_arg_id()};
202  
            fctx.advance_to(dest);
202  
            fctx.advance_to(dest);
203  
            char const* dest1 =
203  
            char const* dest1 =
204  
                pct_vformat(host_chars, pctx, fctx);
204  
                pct_vformat(host_chars, pctx, fctx);
205  
            u.impl_.decoded_[parts::id_host] =
205  
            u.impl_.decoded_[parts::id_host] =
206  
                pct_string_view(dest, dest1 - dest)
206  
                pct_string_view(dest, dest1 - dest)
207  
                    ->decoded_size();
207  
                    ->decoded_size();
208  
        }
208  
        }
209  
        auto uh = u.encoded_host();
209  
        auto uh = u.encoded_host();
210  
        auto h = grammar::parse(uh, host_rule).value();
210  
        auto h = grammar::parse(uh, host_rule).value();
211  
        std::memcpy(
211  
        std::memcpy(
212  
            u.impl_.ip_addr_,
212  
            u.impl_.ip_addr_,
213  
            h.addr,
213  
            h.addr,
214  
            sizeof(u.impl_.ip_addr_));
214  
            sizeof(u.impl_.ip_addr_));
215  
        u.impl_.host_type_ = h.host_type;
215  
        u.impl_.host_type_ = h.host_type;
216  
        if (has_port)
216  
        if (has_port)
217  
        {
217  
        {
218  
            dest = u.set_port_impl(n.port, op);
218  
            dest = u.set_port_impl(n.port, op);
219  
            pctx = {port, pctx.next_arg_id()};
219  
            pctx = {port, pctx.next_arg_id()};
220  
            fctx.advance_to(dest);
220  
            fctx.advance_to(dest);
221  
            char const* dest1 = pct_vformat(
221  
            char const* dest1 = pct_vformat(
222  
                grammar::digit_chars, pctx, fctx);
222  
                grammar::digit_chars, pctx, fctx);
223  
            u.impl_.decoded_[parts::id_port] =
223  
            u.impl_.decoded_[parts::id_port] =
224  
                pct_string_view(dest, dest1 - dest)
224  
                pct_string_view(dest, dest1 - dest)
225  
                    ->decoded_size() + 1;
225  
                    ->decoded_size() + 1;
226  
            core::string_view up = {dest - 1, dest1};
226  
            core::string_view up = {dest - 1, dest1};
227  
            auto p = grammar::parse(up, detail::port_part_rule).value();
227  
            auto p = grammar::parse(up, detail::port_part_rule).value();
228  
            if (p.has_port)
228  
            if (p.has_port)
229  
                u.impl_.port_number_ = p.port_number;
229  
                u.impl_.port_number_ = p.port_number;
230  
        }
230  
        }
231  
    }
231  
    }
232  
    if (!path.empty())
232  
    if (!path.empty())
233  
    {
233  
    {
234  
        auto dest = u.resize_impl(
234  
        auto dest = u.resize_impl(
235  
            parts::id_path,
235  
            parts::id_path,
236  
            n.path, op);
236  
            n.path, op);
237  
        pctx = {path, pctx.next_arg_id()};
237  
        pctx = {path, pctx.next_arg_id()};
238  
        fctx.advance_to(dest);
238  
        fctx.advance_to(dest);
239  
        auto dest1 = pct_vformat(
239  
        auto dest1 = pct_vformat(
240  
            path_chars, pctx, fctx);
240  
            path_chars, pctx, fctx);
241  
        pct_string_view npath(dest, dest1 - dest);
241  
        pct_string_view npath(dest, dest1 - dest);
242  
        u.impl_.decoded_[parts::id_path] +=
242  
        u.impl_.decoded_[parts::id_path] +=
243  
            npath.decoded_size();
243  
            npath.decoded_size();
244  
        if (!npath.empty())
244  
        if (!npath.empty())
245  
        {
245  
        {
246  
            u.impl_.nseg_ = std::count(
246  
            u.impl_.nseg_ = std::count(
247  
                npath.begin() + 1,
247  
                npath.begin() + 1,
248  
                npath.end(), '/') + 1;
248  
                npath.end(), '/') + 1;
249  
        }
249  
        }
250  
        // handle edge cases
250  
        // handle edge cases
251  
        // 1) path is first component and the
251  
        // 1) path is first component and the
252  
        // first segment contains an unencoded ':'
252  
        // first segment contains an unencoded ':'
253  
        // This is impossible because the template
253  
        // This is impossible because the template
254  
        // "{}" would be a host.
254  
        // "{}" would be a host.
255  
        if (u.scheme().empty() &&
255  
        if (u.scheme().empty() &&
256  
            !u.has_authority())
256  
            !u.has_authority())
257  
        {
257  
        {
258  
            auto fseg = u.encoded_segments().front();
258  
            auto fseg = u.encoded_segments().front();
259  
            std::size_t nc = std::count(
259  
            std::size_t nc = std::count(
260  
                fseg.begin(), fseg.end(), ':');
260  
                fseg.begin(), fseg.end(), ':');
261  
            if (nc)
261  
            if (nc)
262  
            {
262  
            {
263  
                std::size_t diff = nc * 2;
263  
                std::size_t diff = nc * 2;
264  
                u.reserve(n_total + diff);
264  
                u.reserve(n_total + diff);
265  
                dest = u.resize_impl(
265  
                dest = u.resize_impl(
266  
                    parts::id_path,
266  
                    parts::id_path,
267  
                    n.path + diff, op);
267  
                    n.path + diff, op);
268  
                char* dest0 = dest + diff;
268  
                char* dest0 = dest + diff;
269  
                std::memmove(dest0, dest, n.path);
269  
                std::memmove(dest0, dest, n.path);
270  
                while (dest0 != dest)
270  
                while (dest0 != dest)
271  
                {
271  
                {
272  
                    if (*dest0 != ':')
272  
                    if (*dest0 != ':')
273  
                    {
273  
                    {
274  
                        *dest++ = *dest0++;
274  
                        *dest++ = *dest0++;
275  
                    }
275  
                    }
276  
                    else
276  
                    else
277  
                    {
277  
                    {
278  
                        *dest++ = '%';
278  
                        *dest++ = '%';
279  
                        *dest++ = '3';
279  
                        *dest++ = '3';
280  
                        *dest++ = 'A';
280  
                        *dest++ = 'A';
281  
                        dest0++;
281  
                        dest0++;
282  
                    }
282  
                    }
283  
                }
283  
                }
284  
            }
284  
            }
285  
        }
285  
        }
286  
        // 2) url has no authority and path
286  
        // 2) url has no authority and path
287  
        // starts with "//"
287  
        // starts with "//"
288  
        if (!u.has_authority() &&
288  
        if (!u.has_authority() &&
289  
            u.encoded_path().starts_with("//"))
289  
            u.encoded_path().starts_with("//"))
290  
        {
290  
        {
291  
            u.reserve(n_total + 2);
291  
            u.reserve(n_total + 2);
292  
            dest = u.resize_impl(
292  
            dest = u.resize_impl(
293  
                parts::id_path,
293  
                parts::id_path,
294  
                n.path + 2, op);
294  
                n.path + 2, op);
295  
            std::memmove(dest + 2, dest, n.path);
295  
            std::memmove(dest + 2, dest, n.path);
296  
            *dest++ = '/';
296  
            *dest++ = '/';
297  
            *dest = '.';
297  
            *dest = '.';
298  
        }
298  
        }
299  
    }
299  
    }
300  
    if (has_query)
300  
    if (has_query)
301  
    {
301  
    {
302  
        auto dest = u.resize_impl(
302  
        auto dest = u.resize_impl(
303  
            parts::id_query,
303  
            parts::id_query,
304  
            n.query + 1, op);
304  
            n.query + 1, op);
305  
        *dest++ = '?';
305  
        *dest++ = '?';
306  
        pctx = {query, pctx.next_arg_id()};
306  
        pctx = {query, pctx.next_arg_id()};
307  
        fctx.advance_to(dest);
307  
        fctx.advance_to(dest);
308  
        auto dest1 = pct_vformat(
308  
        auto dest1 = pct_vformat(
309  
            query_chars, pctx, fctx);
309  
            query_chars, pctx, fctx);
310  
        pct_string_view nquery(dest, dest1 - dest);
310  
        pct_string_view nquery(dest, dest1 - dest);
311  
        u.impl_.decoded_[parts::id_query] +=
311  
        u.impl_.decoded_[parts::id_query] +=
312  
            nquery.decoded_size() + 1;
312  
            nquery.decoded_size() + 1;
313  
        if (!nquery.empty())
313  
        if (!nquery.empty())
314  
        {
314  
        {
315  
            u.impl_.nparam_ = std::count(
315  
            u.impl_.nparam_ = std::count(
316  
                nquery.begin(),
316  
                nquery.begin(),
317  
                nquery.end(), '&') + 1;
317  
                nquery.end(), '&') + 1;
318  
        }
318  
        }
319  
    }
319  
    }
320  
    if (has_frag)
320  
    if (has_frag)
321  
    {
321  
    {
322  
        auto dest = u.resize_impl(
322  
        auto dest = u.resize_impl(
323  
            parts::id_frag,
323  
            parts::id_frag,
324  
            n.frag + 1, op);
324  
            n.frag + 1, op);
325  
        *dest++ = '#';
325  
        *dest++ = '#';
326  
        pctx = {frag, pctx.next_arg_id()};
326  
        pctx = {frag, pctx.next_arg_id()};
327  
        fctx.advance_to(dest);
327  
        fctx.advance_to(dest);
328  
        auto dest1 = pct_vformat(
328  
        auto dest1 = pct_vformat(
329  
            fragment_chars, pctx, fctx);
329  
            fragment_chars, pctx, fctx);
330  
        u.impl_.decoded_[parts::id_frag] +=
330  
        u.impl_.decoded_[parts::id_frag] +=
331  
            make_pct_string_view(
331  
            make_pct_string_view(
332  
                core::string_view(dest, dest1 - dest))
332  
                core::string_view(dest, dest1 - dest))
333  
                ->decoded_size() + 1;
333  
                ->decoded_size() + 1;
334  
    }
334  
    }
335  
}
335  
}
336  

336  

337  
// This rule represents a pct-encoded string
337  
// This rule represents a pct-encoded string
338  
// that contains an arbitrary number of
338  
// that contains an arbitrary number of
339  
// replacement ids in it
339  
// replacement ids in it
340  
template<class CharSet>
340  
template<class CharSet>
341  
struct pct_encoded_fmt_string_rule_t
341  
struct pct_encoded_fmt_string_rule_t
342  
{
342  
{
343  
    using value_type = pct_string_view;
343  
    using value_type = pct_string_view;
344  

344  

345  
    constexpr
345  
    constexpr
346  
    pct_encoded_fmt_string_rule_t(
346  
    pct_encoded_fmt_string_rule_t(
347  
        CharSet const& cs) noexcept
347  
        CharSet const& cs) noexcept
348  
        : cs_(cs)
348  
        : cs_(cs)
349  
    {
349  
    {
350  
    }
350  
    }
351  

351  

352  
    template<class CharSet_>
352  
    template<class CharSet_>
353  
    friend
353  
    friend
354  
    constexpr
354  
    constexpr
355  
    auto
355  
    auto
356  
    pct_encoded_fmt_string_rule(
356  
    pct_encoded_fmt_string_rule(
357  
        CharSet_ const& cs) noexcept ->
357  
        CharSet_ const& cs) noexcept ->
358  
    pct_encoded_fmt_string_rule_t<CharSet_>;
358  
    pct_encoded_fmt_string_rule_t<CharSet_>;
359  

359  

360  
    system::result<value_type>
360  
    system::result<value_type>
361  
    parse(
361  
    parse(
362  
        char const*& it,
362  
        char const*& it,
363  
        char const* end) const noexcept
363  
        char const* end) const noexcept
364  
    {
364  
    {
365  
        auto const start = it;
365  
        auto const start = it;
366  
        if(it == end)
366  
        if(it == end)
367  
        {
367  
        {
368  
            // this might be empty
368  
            // this might be empty
369  
            return {};
369  
            return {};
370  
        }
370  
        }
371  

371  

372  
        // consume some with literal rule
372  
        // consume some with literal rule
373  
        // this might be an empty literal
373  
        // this might be an empty literal
374  
        auto literal_rule = pct_encoded_rule(cs_);
374  
        auto literal_rule = pct_encoded_rule(cs_);
375  
        auto rv = literal_rule.parse(it, end);
375  
        auto rv = literal_rule.parse(it, end);
376  
        while (rv)
376  
        while (rv)
377  
        {
377  
        {
378  
            auto it0 = it;
378  
            auto it0 = it;
379  
            // consume some with replacement id
379  
            // consume some with replacement id
380  
            // rule
380  
            // rule
381  
            if (!replacement_field_rule.parse(it, end))
381  
            if (!replacement_field_rule.parse(it, end))
382  
            {
382  
            {
383  
                it = it0;
383  
                it = it0;
384  
                break;
384  
                break;
385  
            }
385  
            }
386  
            rv = literal_rule.parse(it, end);
386  
            rv = literal_rule.parse(it, end);
387  
        }
387  
        }
388  

388  

389  
        return core::string_view(start, it - start);
389  
        return core::string_view(start, it - start);
390  
    }
390  
    }
391  

391  

392  
private:
392  
private:
393  
    CharSet cs_;
393  
    CharSet cs_;
394  
};
394  
};
395  

395  

396  
template<class CharSet>
396  
template<class CharSet>
397  
constexpr
397  
constexpr
398  
auto
398  
auto
399  
pct_encoded_fmt_string_rule(
399  
pct_encoded_fmt_string_rule(
400  
    CharSet const& cs) noexcept ->
400  
    CharSet const& cs) noexcept ->
401  
    pct_encoded_fmt_string_rule_t<CharSet>
401  
    pct_encoded_fmt_string_rule_t<CharSet>
402  
{
402  
{
403  
    // If an error occurs here it means that
403  
    // If an error occurs here it means that
404  
    // the value of your type does not meet
404  
    // the value of your type does not meet
405  
    // the requirements. Please check the
405  
    // the requirements. Please check the
406  
    // documentation!
406  
    // documentation!
407  
    static_assert(
407  
    static_assert(
408  
        grammar::is_charset<CharSet>::value,
408  
        grammar::is_charset<CharSet>::value,
409  
        "CharSet requirements not met");
409  
        "CharSet requirements not met");
410  

410  

411  
    return pct_encoded_fmt_string_rule_t<CharSet>(cs);
411  
    return pct_encoded_fmt_string_rule_t<CharSet>(cs);
412  
}
412  
}
413  

413  

414  
// This rule represents a regular string with
414  
// This rule represents a regular string with
415  
// only chars from the specified charset and
415  
// only chars from the specified charset and
416  
// an arbitrary number of replacement ids in it
416  
// an arbitrary number of replacement ids in it
417  
template<class CharSet>
417  
template<class CharSet>
418  
struct fmt_token_rule_t
418  
struct fmt_token_rule_t
419  
{
419  
{
420  
    using value_type = pct_string_view;
420  
    using value_type = pct_string_view;
421  

421  

422  
    constexpr
422  
    constexpr
423  
    fmt_token_rule_t(
423  
    fmt_token_rule_t(
424  
        CharSet const& cs) noexcept
424  
        CharSet const& cs) noexcept
425  
        : cs_(cs)
425  
        : cs_(cs)
426  
    {
426  
    {
427  
    }
427  
    }
428  

428  

429  
    template<class CharSet_>
429  
    template<class CharSet_>
430  
    friend
430  
    friend
431  
    constexpr
431  
    constexpr
432  
    auto
432  
    auto
433  
    fmt_token_rule(
433  
    fmt_token_rule(
434  
        CharSet_ const& cs) noexcept ->
434  
        CharSet_ const& cs) noexcept ->
435  
    fmt_token_rule_t<CharSet_>;
435  
    fmt_token_rule_t<CharSet_>;
436  

436  

437  
    system::result<value_type>
437  
    system::result<value_type>
438  
    parse(
438  
    parse(
439  
        char const*& it,
439  
        char const*& it,
440  
        char const* end) const noexcept
440  
        char const* end) const noexcept
441  
    {
441  
    {
442  
        auto const start = it;
442  
        auto const start = it;
443  
        BOOST_ASSERT(it != end);
443  
        BOOST_ASSERT(it != end);
444  
        /*
444  
        /*
445  
        // This should never happen because
445  
        // This should never happen because
446  
        // all tokens are optional and will
446  
        // all tokens are optional and will
447  
        // already return `none`:
447  
        // already return `none`:
448  
        if(it == end)
448  
        if(it == end)
449  
        {
449  
        {
450  
            BOOST_URL_RETURN_EC(
450  
            BOOST_URL_RETURN_EC(
451  
                grammar::error::need_more);
451  
                grammar::error::need_more);
452  
        }
452  
        }
453  
        */
453  
        */
454  

454  

455  
        // consume some with literal rule
455  
        // consume some with literal rule
456  
        // this might be an empty literal
456  
        // this might be an empty literal
457  
        auto partial_token_rule =
457  
        auto partial_token_rule =
458  
            grammar::optional_rule(
458  
            grammar::optional_rule(
459  
                grammar::token_rule(cs_));
459  
                grammar::token_rule(cs_));
460  
        auto rv = partial_token_rule.parse(it, end);
460  
        auto rv = partial_token_rule.parse(it, end);
461  
        while (rv)
461  
        while (rv)
462  
        {
462  
        {
463  
            auto it0 = it;
463  
            auto it0 = it;
464  
            // consume some with replacement id
464  
            // consume some with replacement id
465  
            if (!replacement_field_rule.parse(it, end))
465  
            if (!replacement_field_rule.parse(it, end))
466  
            {
466  
            {
467  
                // no replacement and no more cs
467  
                // no replacement and no more cs
468  
                // before: nothing else to consume
468  
                // before: nothing else to consume
469  
                it = it0;
469  
                it = it0;
470  
                break;
470  
                break;
471  
            }
471  
            }
472  
            // after {...}, consume any more chars
472  
            // after {...}, consume any more chars
473  
            // in the charset
473  
            // in the charset
474  
            rv = partial_token_rule.parse(it, end);
474  
            rv = partial_token_rule.parse(it, end);
475  
        }
475  
        }
476  

476  

477  
        if(it == start)
477  
        if(it == start)
478  
        {
478  
        {
479  
            // it != end but we consumed nothing
479  
            // it != end but we consumed nothing
480  
            BOOST_URL_RETURN_EC(
480  
            BOOST_URL_RETURN_EC(
481  
                grammar::error::need_more);
481  
                grammar::error::need_more);
482  
        }
482  
        }
483  

483  

484  
        return core::string_view(start, it - start);
484  
        return core::string_view(start, it - start);
485  
    }
485  
    }
486  

486  

487  
private:
487  
private:
488  
    CharSet cs_;
488  
    CharSet cs_;
489  
};
489  
};
490  

490  

491  
template<class CharSet>
491  
template<class CharSet>
492  
constexpr
492  
constexpr
493  
auto
493  
auto
494  
fmt_token_rule(
494  
fmt_token_rule(
495  
    CharSet const& cs) noexcept ->
495  
    CharSet const& cs) noexcept ->
496  
    fmt_token_rule_t<CharSet>
496  
    fmt_token_rule_t<CharSet>
497  
{
497  
{
498  
    // If an error occurs here it means that
498  
    // If an error occurs here it means that
499  
    // the value of your type does not meet
499  
    // the value of your type does not meet
500  
    // the requirements. Please check the
500  
    // the requirements. Please check the
501  
    // documentation!
501  
    // documentation!
502  
    static_assert(
502  
    static_assert(
503  
        grammar::is_charset<CharSet>::value,
503  
        grammar::is_charset<CharSet>::value,
504  
        "CharSet requirements not met");
504  
        "CharSet requirements not met");
505  

505  

506  
    return fmt_token_rule_t<CharSet>(cs);
506  
    return fmt_token_rule_t<CharSet>(cs);
507  
}
507  
}
508  

508  

509  
struct userinfo_template_rule_t
509  
struct userinfo_template_rule_t
510  
{
510  
{
511  
    struct value_type
511  
    struct value_type
512  
    {
512  
    {
513  
        core::string_view user;
513  
        core::string_view user;
514  
        core::string_view password;
514  
        core::string_view password;
515  
        bool has_password = false;
515  
        bool has_password = false;
516  
    };
516  
    };
517  

517  

518  
    auto
518  
    auto
519  
    parse(
519  
    parse(
520  
        char const*& it,
520  
        char const*& it,
521  
        char const* end
521  
        char const* end
522  
            ) const noexcept ->
522  
            ) const noexcept ->
523  
        system::result<value_type>
523  
        system::result<value_type>
524  
    {
524  
    {
525  
        static constexpr auto uchars =
525  
        static constexpr auto uchars =
526  
            unreserved_chars +
526  
            unreserved_chars +
527  
            sub_delim_chars;
527  
            sub_delim_chars;
528  
        static constexpr auto pwchars =
528  
        static constexpr auto pwchars =
529  
            uchars + ':';
529  
            uchars + ':';
530  

530  

531  
        value_type t;
531  
        value_type t;
532  

532  

533  
        // user
533  
        // user
534  
        static constexpr auto user_fmt_rule =
534  
        static constexpr auto user_fmt_rule =
535  
            pct_encoded_fmt_string_rule(uchars);
535  
            pct_encoded_fmt_string_rule(uchars);
536  
        auto rv = grammar::parse(
536  
        auto rv = grammar::parse(
537  
            it, end, user_fmt_rule);
537  
            it, end, user_fmt_rule);
538  
        BOOST_ASSERT(rv);
538  
        BOOST_ASSERT(rv);
539  
        t.user = *rv;
539  
        t.user = *rv;
540  

540  

541  
        // ':'
541  
        // ':'
542  
        if( it == end ||
542  
        if( it == end ||
543  
            *it != ':')
543  
            *it != ':')
544  
        {
544  
        {
545  
            t.has_password = false;
545  
            t.has_password = false;
546  
            t.password = {};
546  
            t.password = {};
547  
            return t;
547  
            return t;
548  
        }
548  
        }
549  
        ++it;
549  
        ++it;
550  

550  

551  
        // pass
551  
        // pass
552  
        static constexpr auto pass_fmt_rule =
552  
        static constexpr auto pass_fmt_rule =
553  
            pct_encoded_fmt_string_rule(grammar::ref(pwchars));
553  
            pct_encoded_fmt_string_rule(grammar::ref(pwchars));
554  
        rv = grammar::parse(
554  
        rv = grammar::parse(
555  
            it, end, pass_fmt_rule);
555  
            it, end, pass_fmt_rule);
556  
        BOOST_ASSERT(rv);
556  
        BOOST_ASSERT(rv);
557  
        t.has_password = true;
557  
        t.has_password = true;
558  
        t.password = *rv;
558  
        t.password = *rv;
559  

559  

560  
        return t;
560  
        return t;
561  
    }
561  
    }
562  
};
562  
};
563  

563  

564  
constexpr userinfo_template_rule_t userinfo_template_rule{};
564  
constexpr userinfo_template_rule_t userinfo_template_rule{};
565  

565  

566  
struct host_template_rule_t
566  
struct host_template_rule_t
567  
{
567  
{
568  
    using value_type = core::string_view;
568  
    using value_type = core::string_view;
569  

569  

570  
    auto
570  
    auto
571  
    parse(
571  
    parse(
572  
        char const*& it,
572  
        char const*& it,
573  
        char const* end
573  
        char const* end
574  
            ) const noexcept ->
574  
            ) const noexcept ->
575  
        system::result<value_type>
575  
        system::result<value_type>
576  
    {
576  
    {
577  
        if(it == end)
577  
        if(it == end)
578  
        {
578  
        {
579  
            // empty host
579  
            // empty host
580  
            return {};
580  
            return {};
581  
        }
581  
        }
582  

582  

583  
        // the host type will be ultimately
583  
        // the host type will be ultimately
584  
        // validated when applying the replacement
584  
        // validated when applying the replacement
585  
        // strings. Any chars allowed in hosts
585  
        // strings. Any chars allowed in hosts
586  
        // are allowed here.
586  
        // are allowed here.
587  
        if (*it != '[')
587  
        if (*it != '[')
588  
        {
588  
        {
589  
            // IPv4address and reg-name have the
589  
            // IPv4address and reg-name have the
590  
            // same char sets.
590  
            // same char sets.
591  
            constexpr auto any_host_template_rule =
591  
            constexpr auto any_host_template_rule =
592  
                pct_encoded_fmt_string_rule(host_chars);
592  
                pct_encoded_fmt_string_rule(host_chars);
593  
            auto rv = grammar::parse(
593  
            auto rv = grammar::parse(
594  
                it, end, any_host_template_rule);
594  
                it, end, any_host_template_rule);
595  
            // any_host_template_rule can always
595  
            // any_host_template_rule can always
596  
            // be empty, so it's never invalid
596  
            // be empty, so it's never invalid
597  
            BOOST_ASSERT(rv);
597  
            BOOST_ASSERT(rv);
598  
            return detail::to_sv(*rv);
598  
            return detail::to_sv(*rv);
599  
        }
599  
        }
600  
        // IP-literals need to be enclosed in
600  
        // IP-literals need to be enclosed in
601  
        // "[]" if using ':' in the template
601  
        // "[]" if using ':' in the template
602  
        // string, because the ':' would be
602  
        // string, because the ':' would be
603  
        // ambiguous with the port in fmt string.
603  
        // ambiguous with the port in fmt string.
604  
        // The "[]:" can be used in replacement
604  
        // The "[]:" can be used in replacement
605  
        // strings without the "[]" though.
605  
        // strings without the "[]" though.
606  
        constexpr auto ip_literal_template_rule =
606  
        constexpr auto ip_literal_template_rule =
607  
            pct_encoded_fmt_string_rule(lhost_chars);
607  
            pct_encoded_fmt_string_rule(lhost_chars);
608  
        auto it0 = it;
608  
        auto it0 = it;
609  
        auto rv = grammar::parse(
609  
        auto rv = grammar::parse(
610  
            it, end,
610  
            it, end,
611  
            grammar::optional_rule(
611  
            grammar::optional_rule(
612  
                grammar::tuple_rule(
612  
                grammar::tuple_rule(
613  
                    grammar::squelch(
613  
                    grammar::squelch(
614  
                        grammar::delim_rule('[')),
614  
                        grammar::delim_rule('[')),
615  
                    ip_literal_template_rule,
615  
                    ip_literal_template_rule,
616  
                    grammar::squelch(
616  
                    grammar::squelch(
617  
                        grammar::delim_rule(']')))));
617  
                        grammar::delim_rule(']')))));
618  
        // ip_literal_template_rule can always
618  
        // ip_literal_template_rule can always
619  
        // be empty, so it's never invalid, but
619  
        // be empty, so it's never invalid, but
620  
        // the rule might fail to match the
620  
        // the rule might fail to match the
621  
        // closing "]"
621  
        // closing "]"
622  
        BOOST_ASSERT(rv);
622  
        BOOST_ASSERT(rv);
623  
        return core::string_view{it0, it};
623  
        return core::string_view{it0, it};
624  
    }
624  
    }
625  
};
625  
};
626  

626  

627  
constexpr host_template_rule_t host_template_rule{};
627  
constexpr host_template_rule_t host_template_rule{};
628  

628  

629  
struct authority_template_rule_t
629  
struct authority_template_rule_t
630  
{
630  
{
631  
    using value_type = pattern;
631  
    using value_type = pattern;
632  

632  

633  
    system::result<value_type>
633  
    system::result<value_type>
634  
    parse(
634  
    parse(
635  
        char const*& it,
635  
        char const*& it,
636  
        char const* end
636  
        char const* end
637  
    ) const noexcept
637  
    ) const noexcept
638  
    {
638  
    {
639  
        pattern u;
639  
        pattern u;
640  

640  

641  
        // [ userinfo "@" ]
641  
        // [ userinfo "@" ]
642  
        {
642  
        {
643  
            auto rv = grammar::parse(
643  
            auto rv = grammar::parse(
644  
                it, end,
644  
                it, end,
645  
                grammar::optional_rule(
645  
                grammar::optional_rule(
646  
                    grammar::tuple_rule(
646  
                    grammar::tuple_rule(
647  
                        userinfo_template_rule,
647  
                        userinfo_template_rule,
648  
                        grammar::squelch(
648  
                        grammar::squelch(
649  
                            grammar::delim_rule('@')))));
649  
                            grammar::delim_rule('@')))));
650  
            BOOST_ASSERT(rv);
650  
            BOOST_ASSERT(rv);
651  
            if(rv->has_value())
651  
            if(rv->has_value())
652  
            {
652  
            {
653  
                auto& r = **rv;
653  
                auto& r = **rv;
654  
                u.has_user = true;
654  
                u.has_user = true;
655  
                u.user = r.user;
655  
                u.user = r.user;
656  
                u.has_pass = r.has_password;
656  
                u.has_pass = r.has_password;
657  
                u.pass = r.password;
657  
                u.pass = r.password;
658  
            }
658  
            }
659  
        }
659  
        }
660  

660  

661  
        // host
661  
        // host
662  
        {
662  
        {
663  
            auto rv = grammar::parse(
663  
            auto rv = grammar::parse(
664  
                it, end,
664  
                it, end,
665  
                host_template_rule);
665  
                host_template_rule);
666  
            // host is allowed to be empty
666  
            // host is allowed to be empty
667  
            BOOST_ASSERT(rv);
667  
            BOOST_ASSERT(rv);
668  
            u.host = *rv;
668  
            u.host = *rv;
669  
        }
669  
        }
670  

670  

671  
        // [ ":" port ]
671  
        // [ ":" port ]
672  
        {
672  
        {
673  
            constexpr auto port_template_rule =
673  
            constexpr auto port_template_rule =
674  
                grammar::optional_rule(
674  
                grammar::optional_rule(
675  
                    fmt_token_rule(grammar::digit_chars));
675  
                    fmt_token_rule(grammar::digit_chars));
676  
            auto it0 = it;
676  
            auto it0 = it;
677  
            auto rv = grammar::parse(
677  
            auto rv = grammar::parse(
678  
                it, end,
678  
                it, end,
679  
                grammar::tuple_rule(
679  
                grammar::tuple_rule(
680  
                    grammar::squelch(
680  
                    grammar::squelch(
681  
                        grammar::delim_rule(':')),
681  
                        grammar::delim_rule(':')),
682  
                    port_template_rule));
682  
                    port_template_rule));
683  
            if (!rv)
683  
            if (!rv)
684  
            {
684  
            {
685  
                it = it0;
685  
                it = it0;
686  
            }
686  
            }
687  
            else
687  
            else
688  
            {
688  
            {
689  
                u.has_port = true;
689  
                u.has_port = true;
690  
                if (rv->has_value())
690  
                if (rv->has_value())
691  
                {
691  
                {
692  
                    u.port = **rv;
692  
                    u.port = **rv;
693  
                }
693  
                }
694  
            }
694  
            }
695  
        }
695  
        }
696  

696  

697  
        return u;
697  
        return u;
698  
    }
698  
    }
699  
};
699  
};
700  

700  

701  
constexpr authority_template_rule_t authority_template_rule{};
701  
constexpr authority_template_rule_t authority_template_rule{};
702  

702  

703  
struct scheme_template_rule_t
703  
struct scheme_template_rule_t
704  
{
704  
{
705  
    using value_type = core::string_view;
705  
    using value_type = core::string_view;
706  

706  

707  
    system::result<value_type>
707  
    system::result<value_type>
708  
    parse(
708  
    parse(
709  
        char const*& it,
709  
        char const*& it,
710  
        char const* end) const noexcept
710  
        char const* end) const noexcept
711  
    {
711  
    {
712  
        auto const start = it;
712  
        auto const start = it;
713  
        if(it == end)
713  
        if(it == end)
714  
        {
714  
        {
715  
            // scheme can't be empty
715  
            // scheme can't be empty
716  
            BOOST_URL_RETURN_EC(
716  
            BOOST_URL_RETURN_EC(
717  
                grammar::error::mismatch);
717  
                grammar::error::mismatch);
718  
        }
718  
        }
719  
        if(!grammar::alpha_chars(*it) &&
719  
        if(!grammar::alpha_chars(*it) &&
720  
            *it != '{')
720  
            *it != '{')
721  
        {
721  
        {
722  
            // expected alpha
722  
            // expected alpha
723  
            BOOST_URL_RETURN_EC(
723  
            BOOST_URL_RETURN_EC(
724  
                grammar::error::mismatch);
724  
                grammar::error::mismatch);
725  
        }
725  
        }
726  

726  

727  
        // it starts with replacement id or alpha char
727  
        // it starts with replacement id or alpha char
728  
        if (!grammar::alpha_chars(*it))
728  
        if (!grammar::alpha_chars(*it))
729  
        {
729  
        {
730  
            if (!replacement_field_rule.parse(it, end))
730  
            if (!replacement_field_rule.parse(it, end))
731  
            {
731  
            {
732  
                // replacement_field_rule is invalid
732  
                // replacement_field_rule is invalid
733  
                BOOST_URL_RETURN_EC(
733  
                BOOST_URL_RETURN_EC(
734  
                    grammar::error::mismatch);
734  
                    grammar::error::mismatch);
735  
            }
735  
            }
736  
        }
736  
        }
737  
        else
737  
        else
738  
        {
738  
        {
739  
            // skip first
739  
            // skip first
740  
            ++it;
740  
            ++it;
741  
        }
741  
        }
742  

742  

743  
        static
743  
        static
744  
        constexpr
744  
        constexpr
745  
        grammar::lut_chars scheme_chars(
745  
        grammar::lut_chars scheme_chars(
746  
            "0123456789" "+-."
746  
            "0123456789" "+-."
747  
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
747  
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
748  
            "abcdefghijklmnopqrstuvwxyz");
748  
            "abcdefghijklmnopqrstuvwxyz");
749  

749  

750  
        // non-scheme chars might be a new
750  
        // non-scheme chars might be a new
751  
        // replacement-id or just an invalid char
751  
        // replacement-id or just an invalid char
752  
        it = grammar::find_if_not(
752  
        it = grammar::find_if_not(
753  
            it, end, scheme_chars);
753  
            it, end, scheme_chars);
754  
        while (it != end)
754  
        while (it != end)
755  
        {
755  
        {
756  
            auto it0 = it;
756  
            auto it0 = it;
757  
            if (!replacement_field_rule.parse(it, end))
757  
            if (!replacement_field_rule.parse(it, end))
758  
            {
758  
            {
759  
                it = it0;
759  
                it = it0;
760  
                break;
760  
                break;
761  
            }
761  
            }
762  
            it = grammar::find_if_not(
762  
            it = grammar::find_if_not(
763  
                it, end, scheme_chars);
763  
                it, end, scheme_chars);
764  
        }
764  
        }
765  
        return core::string_view(start, it - start);
765  
        return core::string_view(start, it - start);
766  
    }
766  
    }
767  
};
767  
};
768  

768  

769  
constexpr scheme_template_rule_t scheme_template_rule{};
769  
constexpr scheme_template_rule_t scheme_template_rule{};
770  

770  

771  
// This rule should consider all url types at the
771  
// This rule should consider all url types at the
772  
// same time according to the format string
772  
// same time according to the format string
773  
// - relative urls with no scheme/authority
773  
// - relative urls with no scheme/authority
774  
// - absolute urls have no fragment
774  
// - absolute urls have no fragment
775  
struct pattern_rule_t
775  
struct pattern_rule_t
776  
{
776  
{
777  
    using value_type = pattern;
777  
    using value_type = pattern;
778  

778  

779  
    system::result<value_type>
779  
    system::result<value_type>
780  
    parse(
780  
    parse(
781  
        char const*& it,
781  
        char const*& it,
782  
        char const* const end
782  
        char const* const end
783  
    ) const noexcept
783  
    ) const noexcept
784  
    {
784  
    {
785  
        pattern u;
785  
        pattern u;
786  

786  

787  
        // optional scheme
787  
        // optional scheme
788  
        {
788  
        {
789  
            auto it0 = it;
789  
            auto it0 = it;
790  
            auto rv = grammar::parse(
790  
            auto rv = grammar::parse(
791  
                it, end,
791  
                it, end,
792  
                grammar::tuple_rule(
792  
                grammar::tuple_rule(
793  
                    scheme_template_rule,
793  
                    scheme_template_rule,
794  
                    grammar::squelch(
794  
                    grammar::squelch(
795  
                        grammar::delim_rule(':'))));
795  
                        grammar::delim_rule(':'))));
796  
            if(rv)
796  
            if(rv)
797  
                u.scheme = *rv;
797  
                u.scheme = *rv;
798  
            else
798  
            else
799  
                it = it0;
799  
                it = it0;
800  
        }
800  
        }
801  

801  

802  
        // hier_part (authority + path)
802  
        // hier_part (authority + path)
803  
        // if there are less than 2 chars left,
803  
        // if there are less than 2 chars left,
804  
        // we are parsing the path
804  
        // we are parsing the path
805  
        if (it == end)
805  
        if (it == end)
806  
        {
806  
        {
807  
            // this is over, so we can consider
807  
            // this is over, so we can consider
808  
            // that a "path-empty"
808  
            // that a "path-empty"
809  
            return u;
809  
            return u;
810  
        }
810  
        }
811  
        if(end - it == 1)
811  
        if(end - it == 1)
812  
        {
812  
        {
813  
            // only one char left
813  
            // only one char left
814  
            // it can be a single separator "/",
814  
            // it can be a single separator "/",
815  
            // representing an empty absolute path,
815  
            // representing an empty absolute path,
816  
            // or a single-char segment
816  
            // or a single-char segment
817  
            if(*it == '/')
817  
            if(*it == '/')
818  
            {
818  
            {
819  
                // path-absolute
819  
                // path-absolute
820  
                u.path = {it, 1};
820  
                u.path = {it, 1};
821  
                ++it;
821  
                ++it;
822  
                return u;
822  
                return u;
823  
            }
823  
            }
824  
            // this can be a:
824  
            // this can be a:
825  
            // - path-noscheme if there's no scheme, or
825  
            // - path-noscheme if there's no scheme, or
826  
            // - path-rootless with a single char, or
826  
            // - path-rootless with a single char, or
827  
            // - path-empty (and consume nothing)
827  
            // - path-empty (and consume nothing)
828  
            if (!u.scheme.empty() ||
828  
            if (!u.scheme.empty() ||
829  
                *it != ':')
829  
                *it != ':')
830  
            {
830  
            {
831  
                // path-rootless with a single char
831  
                // path-rootless with a single char
832  
                // this needs to be a segment because
832  
                // this needs to be a segment because
833  
                // the authority needs two slashes
833  
                // the authority needs two slashes
834  
                // "//"
834  
                // "//"
835  
                // path-noscheme also matches here
835  
                // path-noscheme also matches here
836  
                // because we already validated the
836  
                // because we already validated the
837  
                // first char
837  
                // first char
838  
                auto rv = grammar::parse(
838  
                auto rv = grammar::parse(
839  
                    it, end, urls::detail::segment_rule);
839  
                    it, end, urls::detail::segment_rule);
840  
                if(! rv)
840  
                if(! rv)
841  
                    return rv.error();
841  
                    return rv.error();
842  
                u.path = *rv;
842  
                u.path = *rv;
843  
            }
843  
            }
844  
            return u;
844  
            return u;
845  
        }
845  
        }
846  

846  

847  
        // authority
847  
        // authority
848  
        if( it[0] == '/' &&
848  
        if( it[0] == '/' &&
849  
            it[1] == '/')
849  
            it[1] == '/')
850  
        {
850  
        {
851  
            // "//" always indicates authority
851  
            // "//" always indicates authority
852  
            it += 2;
852  
            it += 2;
853  
            auto rv = grammar::parse(
853  
            auto rv = grammar::parse(
854  
                it, end,
854  
                it, end,
855  
                authority_template_rule);
855  
                authority_template_rule);
856  
            // authority is allowed to be empty
856  
            // authority is allowed to be empty
857  
            BOOST_ASSERT(rv);
857  
            BOOST_ASSERT(rv);
858  
            u.has_authority = true;
858  
            u.has_authority = true;
859  
            u.has_user = rv->has_user;
859  
            u.has_user = rv->has_user;
860  
            u.user = rv->user;
860  
            u.user = rv->user;
861  
            u.has_pass = rv->has_pass;
861  
            u.has_pass = rv->has_pass;
862  
            u.pass = rv->pass;
862  
            u.pass = rv->pass;
863  
            u.host = rv->host;
863  
            u.host = rv->host;
864  
            u.has_port = rv->has_port;
864  
            u.has_port = rv->has_port;
865  
            u.port = rv->port;
865  
            u.port = rv->port;
866  
        }
866  
        }
867  

867  

868  
        // the authority requires an absolute path
868  
        // the authority requires an absolute path
869  
        // or an empty path
869  
        // or an empty path
870  
        if (it == end ||
870  
        if (it == end ||
871  
            (u.has_authority &&
871  
            (u.has_authority &&
872  
             (*it != '/' &&
872  
             (*it != '/' &&
873  
              *it != '?' &&
873  
              *it != '?' &&
874  
              *it != '#')))
874  
              *it != '#')))
875  
        {
875  
        {
876  
            // path-empty
876  
            // path-empty
877  
            return u;
877  
            return u;
878  
        }
878  
        }
879  

879  

880  
        // path-abempty
880  
        // path-abempty
881  
        // consume the whole path at once because
881  
        // consume the whole path at once because
882  
        // we're going to count number of segments
882  
        // we're going to count number of segments
883  
        // later after the replacements happen
883  
        // later after the replacements happen
884  
        static constexpr auto segment_fmt_rule =
884  
        static constexpr auto segment_fmt_rule =
885  
            pct_encoded_fmt_string_rule(path_chars);
885  
            pct_encoded_fmt_string_rule(path_chars);
886  
        auto rp = grammar::parse(
886  
        auto rp = grammar::parse(
887  
            it, end, segment_fmt_rule);
887  
            it, end, segment_fmt_rule);
888  
        // path-abempty is allowed to be empty
888  
        // path-abempty is allowed to be empty
889  
        BOOST_ASSERT(rp);
889  
        BOOST_ASSERT(rp);
890  
        u.path = *rp;
890  
        u.path = *rp;
891  

891  

892  
        // [ "?" query ]
892  
        // [ "?" query ]
893  
        {
893  
        {
894  
            static constexpr auto query_fmt_rule =
894  
            static constexpr auto query_fmt_rule =
895  
                pct_encoded_fmt_string_rule(query_chars);
895  
                pct_encoded_fmt_string_rule(query_chars);
896  
            auto rv = grammar::parse(
896  
            auto rv = grammar::parse(
897  
                it, end,
897  
                it, end,
898  
                grammar::tuple_rule(
898  
                grammar::tuple_rule(
899  
                    grammar::squelch(
899  
                    grammar::squelch(
900  
                        grammar::delim_rule('?')),
900  
                        grammar::delim_rule('?')),
901  
                    query_fmt_rule));
901  
                    query_fmt_rule));
902  
            // query is allowed to be empty but
902  
            // query is allowed to be empty but
903  
            // delim rule is not
903  
            // delim rule is not
904  
            if (rv)
904  
            if (rv)
905  
            {
905  
            {
906  
                u.has_query = true;
906  
                u.has_query = true;
907  
                u.query = *rv;
907  
                u.query = *rv;
908  
            }
908  
            }
909  
        }
909  
        }
910  

910  

911  
        // [ "#" fragment ]
911  
        // [ "#" fragment ]
912  
        {
912  
        {
913  
            static constexpr auto frag_fmt_rule =
913  
            static constexpr auto frag_fmt_rule =
914  
                pct_encoded_fmt_string_rule(fragment_chars);
914  
                pct_encoded_fmt_string_rule(fragment_chars);
915  
            auto rv = grammar::parse(
915  
            auto rv = grammar::parse(
916  
                it, end,
916  
                it, end,
917  
                grammar::tuple_rule(
917  
                grammar::tuple_rule(
918  
                    grammar::squelch(
918  
                    grammar::squelch(
919  
                        grammar::delim_rule('#')),
919  
                        grammar::delim_rule('#')),
920  
                    frag_fmt_rule));
920  
                    frag_fmt_rule));
921  
            // frag is allowed to be empty but
921  
            // frag is allowed to be empty but
922  
            // delim rule is not
922  
            // delim rule is not
923  
            if (rv)
923  
            if (rv)
924  
            {
924  
            {
925  
                u.has_frag = true;
925  
                u.has_frag = true;
926  
                u.frag = *rv;
926  
                u.frag = *rv;
927  
            }
927  
            }
928  
        }
928  
        }
929  

929  

930  
        return u;
930  
        return u;
931  
    }
931  
    }
932  
};
932  
};
933  

933  

934  
constexpr pattern_rule_t pattern_rule{};
934  
constexpr pattern_rule_t pattern_rule{};
935  

935  

936  
system::result<pattern>
936  
system::result<pattern>
937  
parse_pattern(
937  
parse_pattern(
938  
    core::string_view s)
938  
    core::string_view s)
939  
{
939  
{
940  
    return grammar::parse(
940  
    return grammar::parse(
941  
        s, pattern_rule);
941  
        s, pattern_rule);
942  
}
942  
}
943  

943  

944  
} // detail
944  
} // detail
945  
} // urls
945  
} // urls
946  
} // boost
946  
} // boost
947  

947