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("{}", "Hello world!").buffer() == "Hello%20world%21");
99 : @endcode
100 :
101 : @note
102 : The formatting machinery relies on language and library
103 : features that are broken on GCC 4.8 and GCC 5.x, so this
104 : function is not supported on those compilers.
105 :
106 : @par Preconditions
107 : All replacement fields must be valid and the
108 : resulting URL should be valid after arguments
109 : are formatted into the URL.
110 :
111 : Because any invalid characters for a URL
112 : component are encoded by this function, only
113 : replacements in the scheme and port components
114 : might be invalid, as these components do not
115 : allow percent-encoding of arbitrary
116 : characters.
117 :
118 : @return A URL holding the formatted result.
119 :
120 : @param fmt The format URL string.
121 : @param args Arguments to be formatted.
122 :
123 : @throws system_error
124 : `fmt` contains an invalid format string and
125 : the result contains an invalid URL after
126 : replacements are applied.
127 :
128 : @par BNF
129 : @code
130 : replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
131 : arg_id ::= integer | identifier
132 : integer ::= digit+
133 : digit ::= "0"..."9"
134 : identifier ::= id_start id_continue*
135 : id_start ::= "a"..."z" | "A"..."Z" | "_"
136 : id_continue ::= id_start | digit
137 : @endcode
138 :
139 : @par Specification
140 : @li <a href="https://fmt.dev/latest/syntax.html"
141 : >Format String Syntax</a>
142 :
143 : @see
144 : @ref format_to,
145 : @ref arg.
146 : */
147 : template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
148 : url
149 147 : format(
150 : core::string_view fmt,
151 : Args&&... args)
152 : {
153 : return detail::vformat(
154 157 : fmt, detail::make_format_args(
155 284 : std::forward<Args>(args)...));
156 : }
157 :
158 : /** Format arguments into a URL
159 :
160 : Format arguments according to the format
161 : URL string into a @ref url_base.
162 :
163 : The rules for a format URL string are the same
164 : as for a `std::format_string`, where replacement
165 : fields are delimited by curly braces.
166 :
167 : The URL components to which replacement fields
168 : belong are identified before replacement is
169 : applied and any invalid characters for that
170 : formatted argument are percent-escaped.
171 :
172 : Hence, the delimiters between URL components,
173 : such as `:`, `//`, `?`, and `#`, should be
174 : included in the URL format string. Likewise,
175 : a format string with a single `"{}"` is
176 : interpreted as a path and any replacement
177 : characters invalid in this component will be
178 : encoded to form a valid URL.
179 :
180 : @par Example
181 : @code
182 : static_url<30> u;
183 : format(u, "{}", "Hello world!");
184 : assert(u.buffer() == "Hello%20world%21");
185 : @endcode
186 :
187 : @par Preconditions
188 : All replacement fields must be valid and the
189 : resulting URL should be valid after arguments
190 : are formatted into the URL.
191 :
192 : Because any invalid characters for a URL
193 : component are encoded by this function, only
194 : replacements in the scheme and port components
195 : might be invalid, as these components do not
196 : allow percent-encoding of arbitrary
197 : characters.
198 :
199 : @par Exception Safety
200 : Strong guarantee.
201 :
202 : @param u An object that derives from @ref url_base.
203 : @param fmt The format URL string.
204 : @param args Arguments to be formatted.
205 :
206 : @throws system_error
207 : `fmt` contains an invalid format string and
208 : `u` contains an invalid URL after replacements
209 : are applied.
210 :
211 : @par BNF
212 : @code
213 : replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
214 : arg_id ::= integer | identifier
215 : integer ::= digit+
216 : digit ::= "0"..."9"
217 : identifier ::= id_start id_continue*
218 : id_start ::= "a"..."z" | "A"..."Z" | "_"
219 : id_continue ::= id_start | digit
220 : @endcode
221 :
222 : @par Specification
223 : @li <a href="https://fmt.dev/latest/syntax.html"
224 : >Format String Syntax</a>
225 :
226 : @see
227 : @ref format.
228 :
229 : */
230 : template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
231 : void
232 4 : format_to(
233 : url_base& u,
234 : core::string_view fmt,
235 : Args&&... args)
236 : {
237 4 : detail::vformat_to(
238 5 : u, fmt, detail::make_format_args(
239 : std::forward<Args>(args)...));
240 3 : }
241 :
242 : /** Format arguments into a URL
243 :
244 : Format arguments according to the format
245 : URL string into a @ref url.
246 :
247 : This overload allows type-erased arguments
248 : to be passed as an initializer_list, which
249 : is mostly convenient for named parameters.
250 :
251 : All arguments must be convertible to a
252 : implementation defined type able to store a
253 : type-erased reference to any valid format
254 : argument.
255 :
256 : The rules for a format URL string are the same
257 : as for a `std::format_string`, where replacement
258 : fields are delimited by curly braces.
259 :
260 : The URL components to which replacement fields
261 : belong are identified before replacement is
262 : applied and any invalid characters for that
263 : formatted argument are percent-escaped.
264 :
265 : Hence, the delimiters between URL components,
266 : such as `:`, `//`, `?`, and `#`, should be
267 : included in the URL format string. Likewise,
268 : a format string with a single `"{}"` is
269 : interpreted as a path and any replacement
270 : characters invalid in this component will be
271 : encoded to form a valid URL.
272 :
273 : @par Example
274 : @code
275 : assert(format("user/{id}", {{"id", 1}}).buffer() == "user/1");
276 : @endcode
277 :
278 : @par Preconditions
279 : All replacement fields must be valid and the
280 : resulting URL should be valid after arguments
281 : are formatted into the URL.
282 :
283 : Because any invalid characters for a URL
284 : component are encoded by this function, only
285 : replacements in the scheme and port components
286 : might be invalid, as these components do not
287 : allow percent-encoding of arbitrary
288 : characters.
289 :
290 : @return A URL holding the formatted result.
291 :
292 : @param fmt The format URL string.
293 : @param args Arguments to be formatted.
294 :
295 : @throws system_error
296 : `fmt` contains an invalid format string and
297 : the result contains an invalid URL after
298 : replacements are applied.
299 :
300 : @par BNF
301 : @code
302 : replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
303 : arg_id ::= integer | identifier
304 : integer ::= digit+
305 : digit ::= "0"..."9"
306 : identifier ::= id_start id_continue*
307 : id_start ::= "a"..."z" | "A"..."Z" | "_"
308 : id_continue ::= id_start | digit
309 : @endcode
310 :
311 : @par Specification
312 : @li <a href="https://fmt.dev/latest/syntax.html"
313 : >Format String Syntax</a>
314 :
315 : @see
316 : @ref format_to.
317 :
318 : */
319 : inline
320 : url
321 3 : format(
322 : core::string_view fmt,
323 : std::initializer_list<format_arg> args)
324 : {
325 : return detail::vformat(
326 : fmt, detail::format_args(
327 3 : args.begin(), args.end()));
328 : }
329 :
330 : /** Format arguments into a URL
331 :
332 : Format arguments according to the format
333 : URL string into a @ref url_base.
334 :
335 : This overload allows type-erased arguments
336 : to be passed as an initializer_list, which
337 : is mostly convenient for named parameters.
338 :
339 : All arguments must be convertible to a
340 : implementation defined type able to store a
341 : type-erased reference to any valid format
342 : argument.
343 :
344 : The rules for a format URL string are the same
345 : as for a `std::format_string`, where replacement
346 : fields are delimited by curly braces.
347 :
348 : The URL components to which replacement fields
349 : belong are identified before replacement is
350 : applied and any invalid characters for that
351 : formatted argument are percent-escaped.
352 :
353 : Hence, the delimiters between URL components,
354 : such as `:`, `//`, `?`, and `#`, should be
355 : included in the URL format string. Likewise,
356 : a format string with a single `"{}"` is
357 : interpreted as a path and any replacement
358 : characters invalid in this component will be
359 : encoded to form a valid URL.
360 :
361 : @par Example
362 : @code
363 : static_url<30> u;
364 : format_to(u, "user/{id}", {{"id", 1}})
365 : assert(u.buffer() == "user/1");
366 : @endcode
367 :
368 : @par Preconditions
369 : All replacement fields must be valid and the
370 : resulting URL should be valid after arguments
371 : are formatted into the URL.
372 :
373 : Because any invalid characters for a URL
374 : component are encoded by this function, only
375 : replacements in the scheme and port components
376 : might be invalid, as these components do not
377 : allow percent-encoding of arbitrary
378 : characters.
379 :
380 : @par Exception Safety
381 : Strong guarantee.
382 :
383 : @param u An object that derives from @ref url_base.
384 : @param fmt The format URL string.
385 : @param args Arguments to be formatted.
386 :
387 : @throws system_error
388 : `fmt` contains an invalid format string and
389 : `u` contains an invalid URL after replacements
390 : are applied.
391 :
392 : @par BNF
393 : @code
394 : replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
395 : arg_id ::= integer | identifier
396 : integer ::= digit+
397 : digit ::= "0"..."9"
398 : identifier ::= id_start id_continue*
399 : id_start ::= "a"..."z" | "A"..."Z" | "_"
400 : id_continue ::= id_start | digit
401 : @endcode
402 :
403 : @par Specification
404 : @li <a href="https://fmt.dev/latest/syntax.html"
405 : >Format String Syntax</a>
406 :
407 : @see
408 : @ref format.
409 :
410 : */
411 : inline
412 : void
413 1 : format_to(
414 : url_base& u,
415 : core::string_view fmt,
416 : std::initializer_list<format_arg> args)
417 : {
418 1 : detail::vformat_to(
419 : u, fmt, detail::format_args(
420 : args.begin(), args.end()));
421 1 : }
422 :
423 : /** Designate a named argument for a replacement field
424 :
425 : Construct a named argument for a format URL
426 : string that contains named replacement fields.
427 :
428 : The function parameters should be convertible
429 : to an implementation defined type able to
430 : store the name and a reference to any type
431 : potentially used as a format argument.
432 :
433 : @par Example
434 : The function should be used to designate a named
435 : argument for a replacement field in a format
436 : URL string.
437 : @code
438 : assert(format("user/{id}", arg("id", 1)).buffer() == "user/1");
439 : @endcode
440 :
441 : @return A temporary object with reference
442 : semantics for a named argument
443 :
444 : @param name The format argument name
445 : @param arg The format argument value
446 :
447 : @see
448 : @ref format,
449 : @ref format_to.
450 :
451 : */
452 : template <class T>
453 : named_arg<T>
454 20 : arg(core::string_view name, T const& arg)
455 : {
456 20 : return {name, arg};
457 : }
458 :
459 : } // url
460 : } // boost
461 :
462 : #endif
|