Line data 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 160 : fmt, detail::make_format_args(
167 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 5 : detail::vformat_to(
251 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 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 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
|