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  
#ifndef BOOST_URL_FORMAT_HPP
10  
#ifndef BOOST_URL_FORMAT_HPP
11  
#define BOOST_URL_FORMAT_HPP
11  
#define BOOST_URL_FORMAT_HPP
12  

12  

13  
#include <boost/url/detail/config.hpp>
13  
#include <boost/url/detail/config.hpp>
14  
#include <boost/core/detail/string_view.hpp>
14  
#include <boost/core/detail/string_view.hpp>
15  
#include <boost/url/url.hpp>
15  
#include <boost/url/url.hpp>
16  
#include <boost/url/detail/vformat.hpp>
16  
#include <boost/url/detail/vformat.hpp>
17  
#include <initializer_list>
17  
#include <initializer_list>
18  

18  

19  
#ifdef BOOST_URL_HAS_CONCEPTS
19  
#ifdef BOOST_URL_HAS_CONCEPTS
20  
#include <concepts>
20  
#include <concepts>
21  
#endif
21  
#endif
22  

22  

23  
namespace boost {
23  
namespace boost {
24  
namespace urls {
24  
namespace urls {
25  

25  

26  
/** A temporary reference to a named formatting argument
26  
/** A temporary reference to a named formatting argument
27  

27  

28  
    This class represents a temporary reference
28  
    This class represents a temporary reference
29  
    to a named formatting argument used by the
29  
    to a named formatting argument used by the
30  
    @ref format function.
30  
    @ref format function.
31  

31  

32  
    Named arguments should always be created
32  
    Named arguments should always be created
33  
    with the @ref arg function.
33  
    with the @ref arg function.
34  

34  

35  
    Any type that can be formatted into a URL
35  
    Any type that can be formatted into a URL
36  
    with the @ref format function can also be used
36  
    with the @ref format function can also be used
37  
    in a named argument. All named arguments
37  
    in a named argument. All named arguments
38  
    are convertible to @ref format_arg and
38  
    are convertible to @ref format_arg and
39  
    can be used in the @ref format function.
39  
    can be used in the @ref format function.
40  

40  

41  
    @see
41  
    @see
42  
        @ref arg,
42  
        @ref arg,
43  
        @ref format,
43  
        @ref format,
44  
        @ref format_to,
44  
        @ref format_to,
45  
        @ref format_arg.
45  
        @ref format_arg.
46  
  */
46  
  */
47  
template <class T>
47  
template <class T>
48  
using named_arg = detail::named_arg<T>;
48  
using named_arg = detail::named_arg<T>;
49  

49  

50  
/** A temporary reference to a formatting argument
50  
/** A temporary reference to a formatting argument
51  

51  

52  
    This class represents a temporary reference
52  
    This class represents a temporary reference
53  
    to a formatting argument used by the
53  
    to a formatting argument used by the
54  
    @ref format function.
54  
    @ref format function.
55  

55  

56  
    A @ref format argument should always be
56  
    A @ref format argument should always be
57  
    created by passing the argument to be
57  
    created by passing the argument to be
58  
    formatted directly to the @ref format function.
58  
    formatted directly to the @ref format function.
59  

59  

60  
    Any type that can be formatted into a URL
60  
    Any type that can be formatted into a URL
61  
    with the @ref format function is convertible
61  
    with the @ref format function is convertible
62  
    to this type.
62  
    to this type.
63  

63  

64  
    This includes basic types, types convertible
64  
    This includes basic types, types convertible
65  
    to `core::string_view`, and @ref named_arg.
65  
    to `core::string_view`, and @ref named_arg.
66  

66  

67  
    @see
67  
    @see
68  
        @ref format,
68  
        @ref format,
69  
        @ref format_to,
69  
        @ref format_to,
70  
        @ref arg.
70  
        @ref arg.
71  
  */
71  
  */
72  
using format_arg = detail::format_arg;
72  
using format_arg = detail::format_arg;
73  

73  

74  
/** Format arguments into a URL
74  
/** Format arguments into a URL
75  

75  

76  
    Format arguments according to the format
76  
    Format arguments according to the format
77  
    URL string into a @ref url.
77  
    URL string into a @ref url.
78  

78  

79  
    The rules for a format URL string are the same
79  
    The rules for a format URL string are the same
80  
    as for a `std::format_string`, where replacement
80  
    as for a `std::format_string`, where replacement
81  
    fields are delimited by curly braces.
81  
    fields are delimited by curly braces.
82  

82  

83  
    The URL components to which replacement fields
83  
    The URL components to which replacement fields
84  
    belong are identified before replacement is
84  
    belong are identified before replacement is
85  
    applied and any invalid characters for that
85  
    applied and any invalid characters for that
86  
    formatted argument are percent-escaped.
86  
    formatted argument are percent-escaped.
87  

87  

88  
    Hence, the delimiters between URL components,
88  
    Hence, the delimiters between URL components,
89  
    such as `:`, `//`, `?`, and `#`, should be
89  
    such as `:`, `//`, `?`, and `#`, should be
90  
    included in the URL format string. Likewise,
90  
    included in the URL format string. Likewise,
91  
    a format string with a single `"{}"` is
91  
    a format string with a single `"{}"` is
92  
    interpreted as a path and any replacement
92  
    interpreted as a path and any replacement
93  
    characters invalid in this component will be
93  
    characters invalid in this component will be
94  
    encoded to form a valid URL.
94  
    encoded to form a valid URL.
95  

95  

96  
    @par Example
96  
    @par Example
97  
    @code
97  
    @code
98 -
    assert(format("{}", "Hello world!").buffer() == "Hello%20world%21");
98 +
    assert(format("{}://{}:{}/rfc/{}",
 
99 +
        "https", "www.ietf.org", 80, "rfc2396.txt"
 
100 +
        ).buffer() == "https://www.ietf.org:80/rfc/rfc2396.txt");
 
101 +
    @endcode
 
102 +

 
103 +
    Arguments that contain special characters are
 
104 +
    automatically percent-encoded for the URL
 
105 +
    component where they appear:
 
106 +

 
107 +
    @code
 
108 +
    assert(format("https://example.com/~{}",
 
109 +
        "John Doe"
 
110 +
        ).buffer() == "https://example.com/~John%20Doe");
99  
    @endcode
111  
    @endcode
100  

112  

101  
    @note
113  
    @note
102  
    The formatting machinery relies on language and library
114  
    The formatting machinery relies on language and library
103  
    features that are broken on GCC 4.8 and GCC 5.x, so this
115  
    features that are broken on GCC 4.8 and GCC 5.x, so this
104  
    function is not supported on those compilers.
116  
    function is not supported on those compilers.
105  

117  

106  
    @par Preconditions
118  
    @par Preconditions
107  
    All replacement fields must be valid and the
119  
    All replacement fields must be valid and the
108  
    resulting URL should be valid after arguments
120  
    resulting URL should be valid after arguments
109  
    are formatted into the URL.
121  
    are formatted into the URL.
110  

122  

111  
    Because any invalid characters for a URL
123  
    Because any invalid characters for a URL
112  
    component are encoded by this function, only
124  
    component are encoded by this function, only
113  
    replacements in the scheme and port components
125  
    replacements in the scheme and port components
114  
    might be invalid, as these components do not
126  
    might be invalid, as these components do not
115  
    allow percent-encoding of arbitrary
127  
    allow percent-encoding of arbitrary
116  
    characters.
128  
    characters.
117  

129  

118  
    @return A URL holding the formatted result.
130  
    @return A URL holding the formatted result.
119  

131  

120  
    @param fmt The format URL string.
132  
    @param fmt The format URL string.
121  
    @param args Arguments to be formatted.
133  
    @param args Arguments to be formatted.
122  

134  

123  
    @throws system_error
135  
    @throws system_error
124  
    `fmt` contains an invalid format string and
136  
    `fmt` contains an invalid format string and
125  
    the result contains an invalid URL after
137  
    the result contains an invalid URL after
126  
    replacements are applied.
138  
    replacements are applied.
127  

139  

128  
    @par BNF
140  
    @par BNF
129  
    @code
141  
    @code
130  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
142  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
131  
    arg_id            ::=  integer | identifier
143  
    arg_id            ::=  integer | identifier
132  
    integer           ::=  digit+
144  
    integer           ::=  digit+
133  
    digit             ::=  "0"..."9"
145  
    digit             ::=  "0"..."9"
134  
    identifier        ::=  id_start id_continue*
146  
    identifier        ::=  id_start id_continue*
135  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
147  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
136  
    id_continue       ::=  id_start | digit
148  
    id_continue       ::=  id_start | digit
137  
    @endcode
149  
    @endcode
138  

150  

139  
    @par Specification
151  
    @par Specification
140  
    @li <a href="https://fmt.dev/latest/syntax.html"
152  
    @li <a href="https://fmt.dev/latest/syntax.html"
141  
        >Format String Syntax</a>
153  
        >Format String Syntax</a>
142  

154  

143  
    @see
155  
    @see
144  
        @ref format_to,
156  
        @ref format_to,
145  
        @ref arg.
157  
        @ref arg.
146  
*/
158  
*/
147  
template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
159  
template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
148  
url
160  
url
149  
format(
161  
format(
150  
    core::string_view fmt,
162  
    core::string_view fmt,
151  
    Args&&... args)
163  
    Args&&... args)
152  
{
164  
{
153  
    return detail::vformat(
165  
    return detail::vformat(
154  
        fmt, detail::make_format_args(
166  
        fmt, detail::make_format_args(
155  
            std::forward<Args>(args)...));
167  
            std::forward<Args>(args)...));
156  
}
168  
}
157  

169  

158  
/** Format arguments into a URL
170  
/** Format arguments into a URL
159  

171  

160  
    Format arguments according to the format
172  
    Format arguments according to the format
161  
    URL string into a @ref url_base.
173  
    URL string into a @ref url_base.
162  

174  

163  
    The rules for a format URL string are the same
175  
    The rules for a format URL string are the same
164  
    as for a `std::format_string`, where replacement
176  
    as for a `std::format_string`, where replacement
165  
    fields are delimited by curly braces.
177  
    fields are delimited by curly braces.
166  

178  

167  
    The URL components to which replacement fields
179  
    The URL components to which replacement fields
168  
    belong are identified before replacement is
180  
    belong are identified before replacement is
169  
    applied and any invalid characters for that
181  
    applied and any invalid characters for that
170  
    formatted argument are percent-escaped.
182  
    formatted argument are percent-escaped.
171  

183  

172  
    Hence, the delimiters between URL components,
184  
    Hence, the delimiters between URL components,
173  
    such as `:`, `//`, `?`, and `#`, should be
185  
    such as `:`, `//`, `?`, and `#`, should be
174  
    included in the URL format string. Likewise,
186  
    included in the URL format string. Likewise,
175  
    a format string with a single `"{}"` is
187  
    a format string with a single `"{}"` is
176  
    interpreted as a path and any replacement
188  
    interpreted as a path and any replacement
177  
    characters invalid in this component will be
189  
    characters invalid in this component will be
178  
    encoded to form a valid URL.
190  
    encoded to form a valid URL.
179  

191  

180  
    @par Example
192  
    @par Example
181  
    @code
193  
    @code
182 -
    static_url<30> u;
194 +
    static_url<50> u;
183 -
    format(u, "{}", "Hello world!");
195 +
    format_to(u, "{}://{}:{}/rfc/{}",
184 -
    assert(u.buffer() == "Hello%20world%21");
196 +
        "https", "www.ietf.org", 80, "rfc2396.txt");
 
197 +
    assert(u.buffer() == "https://www.ietf.org:80/rfc/rfc2396.txt");
185  
    @endcode
198  
    @endcode
186  

199  

187  
    @par Preconditions
200  
    @par Preconditions
188  
    All replacement fields must be valid and the
201  
    All replacement fields must be valid and the
189  
    resulting URL should be valid after arguments
202  
    resulting URL should be valid after arguments
190  
    are formatted into the URL.
203  
    are formatted into the URL.
191  

204  

192  
    Because any invalid characters for a URL
205  
    Because any invalid characters for a URL
193  
    component are encoded by this function, only
206  
    component are encoded by this function, only
194  
    replacements in the scheme and port components
207  
    replacements in the scheme and port components
195  
    might be invalid, as these components do not
208  
    might be invalid, as these components do not
196  
    allow percent-encoding of arbitrary
209  
    allow percent-encoding of arbitrary
197  
    characters.
210  
    characters.
198  

211  

199  
    @par Exception Safety
212  
    @par Exception Safety
200  
    Strong guarantee.
213  
    Strong guarantee.
201  

214  

202  
    @param u An object that derives from @ref url_base.
215  
    @param u An object that derives from @ref url_base.
203  
    @param fmt The format URL string.
216  
    @param fmt The format URL string.
204  
    @param args Arguments to be formatted.
217  
    @param args Arguments to be formatted.
205  

218  

206  
    @throws system_error
219  
    @throws system_error
207  
    `fmt` contains an invalid format string and
220  
    `fmt` contains an invalid format string and
208  
    `u` contains an invalid URL after replacements
221  
    `u` contains an invalid URL after replacements
209  
    are applied.
222  
    are applied.
210  

223  

211  
    @par BNF
224  
    @par BNF
212  
    @code
225  
    @code
213  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
226  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
214  
    arg_id            ::=  integer | identifier
227  
    arg_id            ::=  integer | identifier
215  
    integer           ::=  digit+
228  
    integer           ::=  digit+
216  
    digit             ::=  "0"..."9"
229  
    digit             ::=  "0"..."9"
217  
    identifier        ::=  id_start id_continue*
230  
    identifier        ::=  id_start id_continue*
218  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
231  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
219  
    id_continue       ::=  id_start | digit
232  
    id_continue       ::=  id_start | digit
220  
    @endcode
233  
    @endcode
221  

234  

222  
    @par Specification
235  
    @par Specification
223  
    @li <a href="https://fmt.dev/latest/syntax.html"
236  
    @li <a href="https://fmt.dev/latest/syntax.html"
224  
        >Format String Syntax</a>
237  
        >Format String Syntax</a>
225  

238  

226  
    @see
239  
    @see
227  
        @ref format.
240  
        @ref format.
228  

241  

229  
*/
242  
*/
230  
template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
243  
template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
231  
void
244  
void
232  
format_to(
245  
format_to(
233  
    url_base& u,
246  
    url_base& u,
234  
    core::string_view fmt,
247  
    core::string_view fmt,
235  
    Args&&... args)
248  
    Args&&... args)
236  
{
249  
{
237  
    detail::vformat_to(
250  
    detail::vformat_to(
238  
        u, fmt, detail::make_format_args(
251  
        u, fmt, detail::make_format_args(
239  
            std::forward<Args>(args)...));
252  
            std::forward<Args>(args)...));
240  
}
253  
}
241  

254  

242  
/** Format arguments into a URL
255  
/** Format arguments into a URL
243  

256  

244  
    Format arguments according to the format
257  
    Format arguments according to the format
245  
    URL string into a @ref url.
258  
    URL string into a @ref url.
246  

259  

247  
    This overload allows type-erased arguments
260  
    This overload allows type-erased arguments
248  
    to be passed as an initializer_list, which
261  
    to be passed as an initializer_list, which
249  
    is mostly convenient for named parameters.
262  
    is mostly convenient for named parameters.
250  

263  

251  
    All arguments must be convertible to a
264  
    All arguments must be convertible to a
252  
    implementation defined type able to store a
265  
    implementation defined type able to store a
253  
    type-erased reference to any valid format
266  
    type-erased reference to any valid format
254  
    argument.
267  
    argument.
255  

268  

256  
    The rules for a format URL string are the same
269  
    The rules for a format URL string are the same
257  
    as for a `std::format_string`, where replacement
270  
    as for a `std::format_string`, where replacement
258  
    fields are delimited by curly braces.
271  
    fields are delimited by curly braces.
259  

272  

260  
    The URL components to which replacement fields
273  
    The URL components to which replacement fields
261  
    belong are identified before replacement is
274  
    belong are identified before replacement is
262  
    applied and any invalid characters for that
275  
    applied and any invalid characters for that
263  
    formatted argument are percent-escaped.
276  
    formatted argument are percent-escaped.
264  

277  

265  
    Hence, the delimiters between URL components,
278  
    Hence, the delimiters between URL components,
266  
    such as `:`, `//`, `?`, and `#`, should be
279  
    such as `:`, `//`, `?`, and `#`, should be
267  
    included in the URL format string. Likewise,
280  
    included in the URL format string. Likewise,
268  
    a format string with a single `"{}"` is
281  
    a format string with a single `"{}"` is
269  
    interpreted as a path and any replacement
282  
    interpreted as a path and any replacement
270  
    characters invalid in this component will be
283  
    characters invalid in this component will be
271  
    encoded to form a valid URL.
284  
    encoded to form a valid URL.
272  

285  

273  
    @par Example
286  
    @par Example
274  
    @code
287  
    @code
275 -
    assert(format("user/{id}", {{"id", 1}}).buffer() == "user/1");
288 +
    assert(format(
 
289 +
        "{scheme}://{host}:{port}/{dir}/{file}",
 
290 +
        {{"scheme", "https"}, {"port", 80},
 
291 +
         {"host", "example.com"},
 
292 +
         {"dir", "path/to"},
 
293 +
         {"file", "file.txt"}}
 
294 +
        ).buffer() == "https://example.com:80/path/to/file.txt");
276  
    @endcode
295  
    @endcode
277  

296  

278  
    @par Preconditions
297  
    @par Preconditions
279  
    All replacement fields must be valid and the
298  
    All replacement fields must be valid and the
280  
    resulting URL should be valid after arguments
299  
    resulting URL should be valid after arguments
281  
    are formatted into the URL.
300  
    are formatted into the URL.
282  

301  

283  
    Because any invalid characters for a URL
302  
    Because any invalid characters for a URL
284  
    component are encoded by this function, only
303  
    component are encoded by this function, only
285  
    replacements in the scheme and port components
304  
    replacements in the scheme and port components
286  
    might be invalid, as these components do not
305  
    might be invalid, as these components do not
287  
    allow percent-encoding of arbitrary
306  
    allow percent-encoding of arbitrary
288  
    characters.
307  
    characters.
289  

308  

290  
    @return A URL holding the formatted result.
309  
    @return A URL holding the formatted result.
291  

310  

292  
    @param fmt The format URL string.
311  
    @param fmt The format URL string.
293  
    @param args Arguments to be formatted.
312  
    @param args Arguments to be formatted.
294  

313  

295  
    @throws system_error
314  
    @throws system_error
296  
    `fmt` contains an invalid format string and
315  
    `fmt` contains an invalid format string and
297  
    the result contains an invalid URL after
316  
    the result contains an invalid URL after
298  
    replacements are applied.
317  
    replacements are applied.
299  

318  

300  
    @par BNF
319  
    @par BNF
301  
    @code
320  
    @code
302  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
321  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
303  
    arg_id            ::=  integer | identifier
322  
    arg_id            ::=  integer | identifier
304  
    integer           ::=  digit+
323  
    integer           ::=  digit+
305  
    digit             ::=  "0"..."9"
324  
    digit             ::=  "0"..."9"
306  
    identifier        ::=  id_start id_continue*
325  
    identifier        ::=  id_start id_continue*
307  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
326  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
308  
    id_continue       ::=  id_start | digit
327  
    id_continue       ::=  id_start | digit
309  
    @endcode
328  
    @endcode
310  

329  

311  
    @par Specification
330  
    @par Specification
312  
    @li <a href="https://fmt.dev/latest/syntax.html"
331  
    @li <a href="https://fmt.dev/latest/syntax.html"
313  
        >Format String Syntax</a>
332  
        >Format String Syntax</a>
314  

333  

315  
    @see
334  
    @see
316  
        @ref format_to.
335  
        @ref format_to.
317  

336  

318  
*/
337  
*/
319  
inline
338  
inline
320  
url
339  
url
321  
format(
340  
format(
322  
    core::string_view fmt,
341  
    core::string_view fmt,
323  
    std::initializer_list<format_arg> args)
342  
    std::initializer_list<format_arg> args)
324  
{
343  
{
325  
    return detail::vformat(
344  
    return detail::vformat(
326  
        fmt, detail::format_args(
345  
        fmt, detail::format_args(
327  
            args.begin(), args.end()));
346  
            args.begin(), args.end()));
328  
}
347  
}
329  

348  

330  
/** Format arguments into a URL
349  
/** Format arguments into a URL
331  

350  

332  
    Format arguments according to the format
351  
    Format arguments according to the format
333  
    URL string into a @ref url_base.
352  
    URL string into a @ref url_base.
334  

353  

335  
    This overload allows type-erased arguments
354  
    This overload allows type-erased arguments
336  
    to be passed as an initializer_list, which
355  
    to be passed as an initializer_list, which
337  
    is mostly convenient for named parameters.
356  
    is mostly convenient for named parameters.
338  

357  

339  
    All arguments must be convertible to a
358  
    All arguments must be convertible to a
340  
    implementation defined type able to store a
359  
    implementation defined type able to store a
341  
    type-erased reference to any valid format
360  
    type-erased reference to any valid format
342  
    argument.
361  
    argument.
343  

362  

344  
    The rules for a format URL string are the same
363  
    The rules for a format URL string are the same
345  
    as for a `std::format_string`, where replacement
364  
    as for a `std::format_string`, where replacement
346  
    fields are delimited by curly braces.
365  
    fields are delimited by curly braces.
347  

366  

348  
    The URL components to which replacement fields
367  
    The URL components to which replacement fields
349  
    belong are identified before replacement is
368  
    belong are identified before replacement is
350  
    applied and any invalid characters for that
369  
    applied and any invalid characters for that
351  
    formatted argument are percent-escaped.
370  
    formatted argument are percent-escaped.
352  

371  

353  
    Hence, the delimiters between URL components,
372  
    Hence, the delimiters between URL components,
354  
    such as `:`, `//`, `?`, and `#`, should be
373  
    such as `:`, `//`, `?`, and `#`, should be
355  
    included in the URL format string. Likewise,
374  
    included in the URL format string. Likewise,
356  
    a format string with a single `"{}"` is
375  
    a format string with a single `"{}"` is
357  
    interpreted as a path and any replacement
376  
    interpreted as a path and any replacement
358  
    characters invalid in this component will be
377  
    characters invalid in this component will be
359  
    encoded to form a valid URL.
378  
    encoded to form a valid URL.
360  

379  

361  
    @par Example
380  
    @par Example
362  
    @code
381  
    @code
363 -
    static_url<30> u;
382 +
    url u;
364 -
    format_to(u, "user/{id}", {{"id", 1}})
383 +
    format_to(u,
365 -
    assert(u.buffer() == "user/1");
384 +
        "{scheme}://{host}:{port}/{dir}/{file}",
 
385 +
        {{"scheme", "https"}, {"port", 80},
 
386 +
         {"host", "example.com"},
 
387 +
         {"dir", "path/to"},
 
388 +
         {"file", "file.txt"}});
 
389 +
    assert(u.buffer() == "https://example.com:80/path/to/file.txt");
366  
    @endcode
390  
    @endcode
367  

391  

368  
    @par Preconditions
392  
    @par Preconditions
369  
    All replacement fields must be valid and the
393  
    All replacement fields must be valid and the
370  
    resulting URL should be valid after arguments
394  
    resulting URL should be valid after arguments
371  
    are formatted into the URL.
395  
    are formatted into the URL.
372  

396  

373  
    Because any invalid characters for a URL
397  
    Because any invalid characters for a URL
374  
    component are encoded by this function, only
398  
    component are encoded by this function, only
375  
    replacements in the scheme and port components
399  
    replacements in the scheme and port components
376  
    might be invalid, as these components do not
400  
    might be invalid, as these components do not
377  
    allow percent-encoding of arbitrary
401  
    allow percent-encoding of arbitrary
378  
    characters.
402  
    characters.
379  

403  

380  
    @par Exception Safety
404  
    @par Exception Safety
381  
    Strong guarantee.
405  
    Strong guarantee.
382  

406  

383  
    @param u An object that derives from @ref url_base.
407  
    @param u An object that derives from @ref url_base.
384  
    @param fmt The format URL string.
408  
    @param fmt The format URL string.
385  
    @param args Arguments to be formatted.
409  
    @param args Arguments to be formatted.
386  

410  

387  
    @throws system_error
411  
    @throws system_error
388  
    `fmt` contains an invalid format string and
412  
    `fmt` contains an invalid format string and
389  
    `u` contains an invalid URL after replacements
413  
    `u` contains an invalid URL after replacements
390  
    are applied.
414  
    are applied.
391  

415  

392  
    @par BNF
416  
    @par BNF
393  
    @code
417  
    @code
394  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
418  
    replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
395  
    arg_id            ::=  integer | identifier
419  
    arg_id            ::=  integer | identifier
396  
    integer           ::=  digit+
420  
    integer           ::=  digit+
397  
    digit             ::=  "0"..."9"
421  
    digit             ::=  "0"..."9"
398  
    identifier        ::=  id_start id_continue*
422  
    identifier        ::=  id_start id_continue*
399  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
423  
    id_start          ::=  "a"..."z" | "A"..."Z" | "_"
400  
    id_continue       ::=  id_start | digit
424  
    id_continue       ::=  id_start | digit
401  
    @endcode
425  
    @endcode
402  

426  

403  
    @par Specification
427  
    @par Specification
404  
    @li <a href="https://fmt.dev/latest/syntax.html"
428  
    @li <a href="https://fmt.dev/latest/syntax.html"
405  
        >Format String Syntax</a>
429  
        >Format String Syntax</a>
406  

430  

407  
    @see
431  
    @see
408  
        @ref format.
432  
        @ref format.
409  

433  

410  
*/
434  
*/
411  
inline
435  
inline
412  
void
436  
void
413  
format_to(
437  
format_to(
414  
    url_base& u,
438  
    url_base& u,
415  
    core::string_view fmt,
439  
    core::string_view fmt,
416  
    std::initializer_list<format_arg> args)
440  
    std::initializer_list<format_arg> args)
417  
{
441  
{
418  
    detail::vformat_to(
442  
    detail::vformat_to(
419  
        u, fmt, detail::format_args(
443  
        u, fmt, detail::format_args(
420  
            args.begin(), args.end()));
444  
            args.begin(), args.end()));
421  
}
445  
}
422  

446  

423  
/** Designate a named argument for a replacement field
447  
/** Designate a named argument for a replacement field
424  

448  

425  
    Construct a named argument for a format URL
449  
    Construct a named argument for a format URL
426  
    string that contains named replacement fields.
450  
    string that contains named replacement fields.
427  

451  

428  
    The function parameters should be convertible
452  
    The function parameters should be convertible
429  
    to an implementation defined type able to
453  
    to an implementation defined type able to
430  
    store the name and a reference to any type
454  
    store the name and a reference to any type
431  
    potentially used as a format argument.
455  
    potentially used as a format argument.
432  

456  

433 -
    The function should be used to designate a named
 
434 -
    argument for a replacement field in a format
 
435 -
    URL string.
 
436  
    @par Example
457  
    @par Example
437  
    @code
458  
    @code
438 -
    assert(format("user/{id}", arg("id", 1)).buffer() == "user/1");
459 +
    assert(format(
 
460 +
        "https://example.com/~{username}",
 
461 +
        arg("username", "mark")
 
462 +
        ).buffer() == "https://example.com/~mark");
439  
    @endcode
463  
    @endcode
440  

464  

441  
    @return A temporary object with reference
465  
    @return A temporary object with reference
442  
    semantics for a named argument
466  
    semantics for a named argument
443  

467  

444  
    @param name The format argument name
468  
    @param name The format argument name
445  
    @param arg The format argument value
469  
    @param arg The format argument value
446  

470  

447  
    @see
471  
    @see
448  
        @ref format,
472  
        @ref format,
449  
        @ref format_to.
473  
        @ref format_to.
450  

474  

451  
*/
475  
*/
452  
template <class T>
476  
template <class T>
453  
named_arg<T>
477  
named_arg<T>
454  
arg(core::string_view name, T const& arg)
478  
arg(core::string_view name, T const& arg)
455  
{
479  
{
456  
    return {name, arg};
480  
    return {name, arg};
457  
}
481  
}
458  

482  

459  
} // url
483  
} // url
460  
} // boost
484  
} // boost
461  

485  

462  
#endif
486  
#endif