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