include/boost/url/format.hpp

100.0% Lines (14/14) 100.0% Functions (65/65) 100.0% Branches (14/14)
include/boost/url/format.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
3 //
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)
6 //
7 // Official repository: https://github.com/boostorg/url
8 //
9
10 #ifndef BOOST_URL_FORMAT_HPP
11 #define BOOST_URL_FORMAT_HPP
12
13 #include <boost/url/detail/config.hpp>
14 #include <boost/core/detail/string_view.hpp>
15 #include <boost/url/url.hpp>
16 #include <boost/url/detail/vformat.hpp>
17 #include <initializer_list>
18
19 #ifdef BOOST_URL_HAS_CONCEPTS
20 #include <concepts>
21 #endif
22
23 namespace boost {
24 namespace urls {
25
26 /** A temporary reference to a named formatting argument
27
28 This class represents a temporary reference
29 to a named formatting argument used by the
30 @ref format function.
31
32 Named arguments should always be created
33 with the @ref arg function.
34
35 Any type that can be formatted into a URL
36 with the @ref format function can also be used
37 in a named argument. All named arguments
38 are convertible to @ref format_arg and
39 can be used in the @ref format function.
40
41 @see
42 @ref arg,
43 @ref format,
44 @ref format_to,
45 @ref format_arg.
46 */
47 template <class T>
48 using named_arg = detail::named_arg<T>;
49
50 /** A temporary reference to a formatting argument
51
52 This class represents a temporary reference
53 to a formatting argument used by the
54 @ref format function.
55
56 A @ref format argument should always be
57 created by passing the argument to be
58 formatted directly to the @ref format function.
59
60 Any type that can be formatted into a URL
61 with the @ref format function is convertible
62 to this type.
63
64 This includes basic types, types convertible
65 to `core::string_view`, and @ref named_arg.
66
67 @see
68 @ref format,
69 @ref format_to,
70 @ref arg.
71 */
72 using format_arg = detail::format_arg;
73
74 /** Format arguments into a URL
75
76 Format arguments according to the format
77 URL string into a @ref url.
78
79 The rules for a format URL string are the same
80 as for a `std::format_string`, where replacement
81 fields are delimited by curly braces.
82
83 The URL components to which replacement fields
84 belong are identified before replacement is
85 applied and any invalid characters for that
86 formatted argument are percent-escaped.
87
88 Hence, the delimiters between URL components,
89 such as `:`, `//`, `?`, and `#`, should be
90 included in the URL format string. Likewise,
91 a format string with a single `"{}"` is
92 interpreted as a path and any replacement
93 characters invalid in this component will be
94 encoded to form a valid URL.
95
96 @par Example
97 @code
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");
111 @endcode
112
113 @note
114 The formatting machinery relies on language and library
115 features that are broken on GCC 4.8 and GCC 5.x, so this
116 function is not supported on those compilers.
117
118 @par Preconditions
119 All replacement fields must be valid and the
120 resulting URL should be valid after arguments
121 are formatted into the URL.
122
123 Because any invalid characters for a URL
124 component are encoded by this function, only
125 replacements in the scheme and port components
126 might be invalid, as these components do not
127 allow percent-encoding of arbitrary
128 characters.
129
130 @return A URL holding the formatted result.
131
132 @param fmt The format URL string.
133 @param args Arguments to be formatted.
134
135 @throws system_error
136 `fmt` contains an invalid format string and
137 the result contains an invalid URL after
138 replacements are applied.
139
140 @par BNF
141 @code
142 replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
143 arg_id ::= integer | identifier
144 integer ::= digit+
145 digit ::= "0"..."9"
146 identifier ::= id_start id_continue*
147 id_start ::= "a"..."z" | "A"..."Z" | "_"
148 id_continue ::= id_start | digit
149 @endcode
150
151 @par Specification
152 @li <a href="https://fmt.dev/latest/syntax.html"
153 >Format String Syntax</a>
154
155 @see
156 @ref format_to,
157 @ref arg.
158 */
159 template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
160 url
161 150 format(
162 core::string_view fmt,
163 Args&&... args)
164 {
165 return detail::vformat(
166
7/7
✓ Branch 2 taken 68 times.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 3 times.
✓ Branch 8 taken 1 time.
✓ Branch 10 taken 1 time.
160 fmt, detail::make_format_args(
167
2/2
✓ Branch 2 taken 128 times.
✓ Branch 2 taken 12 times.
290 std::forward<Args>(args)...));
168 }
169
170 /** Format arguments into a URL
171
172 Format arguments according to the format
173 URL string into a @ref url_base.
174
175 The rules for a format URL string are the same
176 as for a `std::format_string`, where replacement
177 fields are delimited by curly braces.
178
179 The URL components to which replacement fields
180 belong are identified before replacement is
181 applied and any invalid characters for that
182 formatted argument are percent-escaped.
183
184 Hence, the delimiters between URL components,
185 such as `:`, `//`, `?`, and `#`, should be
186 included in the URL format string. Likewise,
187 a format string with a single `"{}"` is
188 interpreted as a path and any replacement
189 characters invalid in this component will be
190 encoded to form a valid URL.
191
192 @par Example
193 @code
194 static_url<50> u;
195 format_to(u, "{}://{}:{}/rfc/{}",
196 "https", "www.ietf.org", 80, "rfc2396.txt");
197 assert(u.buffer() == "https://www.ietf.org:80/rfc/rfc2396.txt");
198 @endcode
199
200 @par Preconditions
201 All replacement fields must be valid and the
202 resulting URL should be valid after arguments
203 are formatted into the URL.
204
205 Because any invalid characters for a URL
206 component are encoded by this function, only
207 replacements in the scheme and port components
208 might be invalid, as these components do not
209 allow percent-encoding of arbitrary
210 characters.
211
212 @par Exception Safety
213 Strong guarantee.
214
215 @param u An object that derives from @ref url_base.
216 @param fmt The format URL string.
217 @param args Arguments to be formatted.
218
219 @throws system_error
220 `fmt` contains an invalid format string and
221 `u` contains an invalid URL after replacements
222 are applied.
223
224 @par BNF
225 @code
226 replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
227 arg_id ::= integer | identifier
228 integer ::= digit+
229 digit ::= "0"..."9"
230 identifier ::= id_start id_continue*
231 id_start ::= "a"..."z" | "A"..."Z" | "_"
232 id_continue ::= id_start | digit
233 @endcode
234
235 @par Specification
236 @li <a href="https://fmt.dev/latest/syntax.html"
237 >Format String Syntax</a>
238
239 @see
240 @ref format.
241
242 */
243 template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
244 void
245 5 format_to(
246 url_base& u,
247 core::string_view fmt,
248 Args&&... args)
249 {
250
1/1
✓ Branch 2 taken 4 times.
5 detail::vformat_to(
251
2/2
✓ Branch 3 taken 2 times.
✓ Branch 5 taken 3 times.
6 u, fmt, detail::make_format_args(
252 std::forward<Args>(args)...));
253 4 }
254
255 /** Format arguments into a URL
256
257 Format arguments according to the format
258 URL string into a @ref url.
259
260 This overload allows type-erased arguments
261 to be passed as an initializer_list, which
262 is mostly convenient for named parameters.
263
264 All arguments must be convertible to a
265 implementation defined type able to store a
266 type-erased reference to any valid format
267 argument.
268
269 The rules for a format URL string are the same
270 as for a `std::format_string`, where replacement
271 fields are delimited by curly braces.
272
273 The URL components to which replacement fields
274 belong are identified before replacement is
275 applied and any invalid characters for that
276 formatted argument are percent-escaped.
277
278 Hence, the delimiters between URL components,
279 such as `:`, `//`, `?`, and `#`, should be
280 included in the URL format string. Likewise,
281 a format string with a single `"{}"` is
282 interpreted as a path and any replacement
283 characters invalid in this component will be
284 encoded to form a valid URL.
285
286 @par Example
287 @code
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");
295 @endcode
296
297 @par Preconditions
298 All replacement fields must be valid and the
299 resulting URL should be valid after arguments
300 are formatted into the URL.
301
302 Because any invalid characters for a URL
303 component are encoded by this function, only
304 replacements in the scheme and port components
305 might be invalid, as these components do not
306 allow percent-encoding of arbitrary
307 characters.
308
309 @return A URL holding the formatted result.
310
311 @param fmt The format URL string.
312 @param args Arguments to be formatted.
313
314 @throws system_error
315 `fmt` contains an invalid format string and
316 the result contains an invalid URL after
317 replacements are applied.
318
319 @par BNF
320 @code
321 replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
322 arg_id ::= integer | identifier
323 integer ::= digit+
324 digit ::= "0"..."9"
325 identifier ::= id_start id_continue*
326 id_start ::= "a"..."z" | "A"..."Z" | "_"
327 id_continue ::= id_start | digit
328 @endcode
329
330 @par Specification
331 @li <a href="https://fmt.dev/latest/syntax.html"
332 >Format String Syntax</a>
333
334 @see
335 @ref format_to.
336
337 */
338 inline
339 url
340 4 format(
341 core::string_view fmt,
342 std::initializer_list<format_arg> args)
343 {
344 return detail::vformat(
345 fmt, detail::format_args(
346
1/1
✓ Branch 4 taken 4 times.
4 args.begin(), args.end()));
347 }
348
349 /** Format arguments into a URL
350
351 Format arguments according to the format
352 URL string into a @ref url_base.
353
354 This overload allows type-erased arguments
355 to be passed as an initializer_list, which
356 is mostly convenient for named parameters.
357
358 All arguments must be convertible to a
359 implementation defined type able to store a
360 type-erased reference to any valid format
361 argument.
362
363 The rules for a format URL string are the same
364 as for a `std::format_string`, where replacement
365 fields are delimited by curly braces.
366
367 The URL components to which replacement fields
368 belong are identified before replacement is
369 applied and any invalid characters for that
370 formatted argument are percent-escaped.
371
372 Hence, the delimiters between URL components,
373 such as `:`, `//`, `?`, and `#`, should be
374 included in the URL format string. Likewise,
375 a format string with a single `"{}"` is
376 interpreted as a path and any replacement
377 characters invalid in this component will be
378 encoded to form a valid URL.
379
380 @par Example
381 @code
382 url u;
383 format_to(u,
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");
390 @endcode
391
392 @par Preconditions
393 All replacement fields must be valid and the
394 resulting URL should be valid after arguments
395 are formatted into the URL.
396
397 Because any invalid characters for a URL
398 component are encoded by this function, only
399 replacements in the scheme and port components
400 might be invalid, as these components do not
401 allow percent-encoding of arbitrary
402 characters.
403
404 @par Exception Safety
405 Strong guarantee.
406
407 @param u An object that derives from @ref url_base.
408 @param fmt The format URL string.
409 @param args Arguments to be formatted.
410
411 @throws system_error
412 `fmt` contains an invalid format string and
413 `u` contains an invalid URL after replacements
414 are applied.
415
416 @par BNF
417 @code
418 replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
419 arg_id ::= integer | identifier
420 integer ::= digit+
421 digit ::= "0"..."9"
422 identifier ::= id_start id_continue*
423 id_start ::= "a"..."z" | "A"..."Z" | "_"
424 id_continue ::= id_start | digit
425 @endcode
426
427 @par Specification
428 @li <a href="https://fmt.dev/latest/syntax.html"
429 >Format String Syntax</a>
430
431 @see
432 @ref format.
433
434 */
435 inline
436 void
437 2 format_to(
438 url_base& u,
439 core::string_view fmt,
440 std::initializer_list<format_arg> args)
441 {
442
1/1
✓ Branch 4 taken 2 times.
2 detail::vformat_to(
443 u, fmt, detail::format_args(
444 args.begin(), args.end()));
445 2 }
446
447 /** Designate a named argument for a replacement field
448
449 Construct a named argument for a format URL
450 string that contains named replacement fields.
451
452 The function parameters should be convertible
453 to an implementation defined type able to
454 store the name and a reference to any type
455 potentially used as a format argument.
456
457 @par Example
458 @code
459 assert(format(
460 "https://example.com/~{username}",
461 arg("username", "mark")
462 ).buffer() == "https://example.com/~mark");
463 @endcode
464
465 @return A temporary object with reference
466 semantics for a named argument
467
468 @param name The format argument name
469 @param arg The format argument value
470
471 @see
472 @ref format,
473 @ref format_to.
474
475 */
476 template <class T>
477 named_arg<T>
478 21 arg(core::string_view name, T const& arg)
479 {
480 21 return {name, arg};
481 }
482
483 } // url
484 } // boost
485
486 #endif
487