format.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. #ifndef BOOST_URL_FORMAT_HPP
  10. #define BOOST_URL_FORMAT_HPP
  11. #include <boost/url/detail/config.hpp>
  12. #include <boost/core/detail/string_view.hpp>
  13. #include <boost/url/url.hpp>
  14. #include <boost/url/detail/vformat.hpp>
  15. #include <initializer_list>
  16. #ifdef BOOST_URL_HAS_CONCEPTS
  17. #include <concepts>
  18. #endif
  19. namespace boost {
  20. namespace urls {
  21. /** A temporary reference to a named formatting argument
  22. This class represents a temporary reference
  23. to a named formatting argument used by the
  24. @ref format function.
  25. Named arguments should always be created
  26. with the @ref arg function.
  27. Any type that can be formatted into a URL
  28. with the @ref format function can also be used
  29. in a named argument. All named arguments
  30. are convertible to @ref format_arg and
  31. can be used in the @ref format function.
  32. @see
  33. @ref arg,
  34. @ref format,
  35. @ref format_to,
  36. @ref format_arg.
  37. */
  38. template <class T>
  39. using named_arg = detail::named_arg<T>;
  40. /** A temporary reference to a formatting argument
  41. This class represents a temporary reference
  42. to a formatting argument used by the
  43. @ref format function.
  44. A @ref format argument should always be
  45. created by passing the argument to be
  46. formatted directly to the @ref format function.
  47. Any type that can be formatted into a URL
  48. with the @ref format function is convertible
  49. to this type.
  50. This includes basic types, types convertible
  51. to `core::string_view`, and @ref named_arg.
  52. @see
  53. @ref format,
  54. @ref format_to,
  55. @ref arg.
  56. */
  57. using format_arg = detail::format_arg;
  58. /** Format arguments into a URL
  59. Format arguments according to the format
  60. URL string into a @ref url.
  61. The rules for a format URL string are the same
  62. as for a `std::format_string`, where replacement
  63. fields are delimited by curly braces.
  64. The URL components to which replacement fields
  65. belong are identified before replacement is
  66. applied and any invalid characters for that
  67. formatted argument are percent-escaped.
  68. Hence, the delimiters between URL components,
  69. such as `:`, `//`, `?`, and `#`, should be
  70. included in the URL format string. Likewise,
  71. a format string with a single `"{}"` is
  72. interpreted as a path and any replacement
  73. characters invalid in this component will be
  74. encoded to form a valid URL.
  75. @par Example
  76. @code
  77. assert(format("{}", "Hello world!").buffer() == "Hello%20world%21");
  78. @endcode
  79. @par Preconditions
  80. All replacement fields must be valid and the
  81. resulting URL should be valid after arguments
  82. are formatted into the URL.
  83. Because any invalid characters for a URL
  84. component are encoded by this function, only
  85. replacements in the scheme and port components
  86. might be invalid, as these components do not
  87. allow percent-encoding of arbitrary
  88. characters.
  89. @return A URL holding the formatted result.
  90. @param fmt The format URL string.
  91. @param args Arguments to be formatted.
  92. @throws system_error
  93. `fmt` contains an invalid format string and
  94. the result contains an invalid URL after
  95. replacements are applied.
  96. @par BNF
  97. @code
  98. replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
  99. arg_id ::= integer | identifier
  100. integer ::= digit+
  101. digit ::= "0"..."9"
  102. identifier ::= id_start id_continue*
  103. id_start ::= "a"..."z" | "A"..."Z" | "_"
  104. id_continue ::= id_start | digit
  105. @endcode
  106. @par Specification
  107. @li <a href="https://fmt.dev/latest/syntax.html"
  108. >Format String Syntax</a>
  109. @see
  110. @ref format_to,
  111. @ref arg.
  112. */
  113. template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
  114. url
  115. format(
  116. core::string_view fmt,
  117. Args&&... args)
  118. {
  119. return detail::vformat(
  120. fmt, detail::make_format_args(
  121. std::forward<Args>(args)...));
  122. }
  123. /** Format arguments into a URL
  124. Format arguments according to the format
  125. URL string into a @ref url_base.
  126. The rules for a format URL string are the same
  127. as for a `std::format_string`, where replacement
  128. fields are delimited by curly braces.
  129. The URL components to which replacement fields
  130. belong are identified before replacement is
  131. applied and any invalid characters for that
  132. formatted argument are percent-escaped.
  133. Hence, the delimiters between URL components,
  134. such as `:`, `//`, `?`, and `#`, should be
  135. included in the URL format string. Likewise,
  136. a format string with a single `"{}"` is
  137. interpreted as a path and any replacement
  138. characters invalid in this component will be
  139. encoded to form a valid URL.
  140. @par Example
  141. @code
  142. static_url<30> u;
  143. format(u, "{}", "Hello world!");
  144. assert(u.buffer() == "Hello%20world%21");
  145. @endcode
  146. @par Preconditions
  147. All replacement fields must be valid and the
  148. resulting URL should be valid after arguments
  149. are formatted into the URL.
  150. Because any invalid characters for a URL
  151. component are encoded by this function, only
  152. replacements in the scheme and port components
  153. might be invalid, as these components do not
  154. allow percent-encoding of arbitrary
  155. characters.
  156. @par Exception Safety
  157. Strong guarantee.
  158. @param u An object that derives from @ref url_base.
  159. @param fmt The format URL string.
  160. @param args Arguments to be formatted.
  161. @throws system_error
  162. `fmt` contains an invalid format string and
  163. `u` contains an invalid URL after replacements
  164. are applied.
  165. @par BNF
  166. @code
  167. replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
  168. arg_id ::= integer | identifier
  169. integer ::= digit+
  170. digit ::= "0"..."9"
  171. identifier ::= id_start id_continue*
  172. id_start ::= "a"..."z" | "A"..."Z" | "_"
  173. id_continue ::= id_start | digit
  174. @endcode
  175. @par Specification
  176. @li <a href="https://fmt.dev/latest/syntax.html"
  177. >Format String Syntax</a>
  178. @see
  179. @ref format.
  180. */
  181. template <BOOST_URL_CONSTRAINT(std::convertible_to<format_arg>)... Args>
  182. void
  183. format_to(
  184. url_base& u,
  185. core::string_view fmt,
  186. Args&&... args)
  187. {
  188. detail::vformat_to(
  189. u, fmt, detail::make_format_args(
  190. std::forward<Args>(args)...));
  191. }
  192. /** Format arguments into a URL
  193. Format arguments according to the format
  194. URL string into a @ref url.
  195. This overload allows type-erased arguments
  196. to be passed as an initializer_list, which
  197. is mostly convenient for named parameters.
  198. All arguments must be convertible to a
  199. implementation defined type able to store a
  200. type-erased reference to any valid format
  201. argument.
  202. The rules for a format URL string are the same
  203. as for a `std::format_string`, where replacement
  204. fields are delimited by curly braces.
  205. The URL components to which replacement fields
  206. belong are identified before replacement is
  207. applied and any invalid characters for that
  208. formatted argument are percent-escaped.
  209. Hence, the delimiters between URL components,
  210. such as `:`, `//`, `?`, and `#`, should be
  211. included in the URL format string. Likewise,
  212. a format string with a single `"{}"` is
  213. interpreted as a path and any replacement
  214. characters invalid in this component will be
  215. encoded to form a valid URL.
  216. @par Example
  217. @code
  218. assert(format("user/{id}", {{"id", 1}}).buffer() == "user/1");
  219. @endcode
  220. @par Preconditions
  221. All replacement fields must be valid and the
  222. resulting URL should be valid after arguments
  223. are formatted into the URL.
  224. Because any invalid characters for a URL
  225. component are encoded by this function, only
  226. replacements in the scheme and port components
  227. might be invalid, as these components do not
  228. allow percent-encoding of arbitrary
  229. characters.
  230. @return A URL holding the formatted result.
  231. @param fmt The format URL string.
  232. @param args Arguments to be formatted.
  233. @throws system_error
  234. `fmt` contains an invalid format string and
  235. the result contains an invalid URL after
  236. replacements are applied.
  237. @par BNF
  238. @code
  239. replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
  240. arg_id ::= integer | identifier
  241. integer ::= digit+
  242. digit ::= "0"..."9"
  243. identifier ::= id_start id_continue*
  244. id_start ::= "a"..."z" | "A"..."Z" | "_"
  245. id_continue ::= id_start | digit
  246. @endcode
  247. @par Specification
  248. @li <a href="https://fmt.dev/latest/syntax.html"
  249. >Format String Syntax</a>
  250. @see
  251. @ref format_to.
  252. */
  253. inline
  254. url
  255. format(
  256. core::string_view fmt,
  257. std::initializer_list<format_arg> args)
  258. {
  259. return detail::vformat(
  260. fmt, detail::format_args(
  261. args.begin(), args.end()));
  262. }
  263. /** Format arguments into a URL
  264. Format arguments according to the format
  265. URL string into a @ref url_base.
  266. This overload allows type-erased arguments
  267. to be passed as an initializer_list, which
  268. is mostly convenient for named parameters.
  269. All arguments must be convertible to a
  270. implementation defined type able to store a
  271. type-erased reference to any valid format
  272. argument.
  273. The rules for a format URL string are the same
  274. as for a `std::format_string`, where replacement
  275. fields are delimited by curly braces.
  276. The URL components to which replacement fields
  277. belong are identified before replacement is
  278. applied and any invalid characters for that
  279. formatted argument are percent-escaped.
  280. Hence, the delimiters between URL components,
  281. such as `:`, `//`, `?`, and `#`, should be
  282. included in the URL format string. Likewise,
  283. a format string with a single `"{}"` is
  284. interpreted as a path and any replacement
  285. characters invalid in this component will be
  286. encoded to form a valid URL.
  287. @par Example
  288. @code
  289. static_url<30> u;
  290. format_to(u, "user/{id}", {{"id", 1}})
  291. assert(u.buffer() == "user/1");
  292. @endcode
  293. @par Preconditions
  294. All replacement fields must be valid and the
  295. resulting URL should be valid after arguments
  296. are formatted into the URL.
  297. Because any invalid characters for a URL
  298. component are encoded by this function, only
  299. replacements in the scheme and port components
  300. might be invalid, as these components do not
  301. allow percent-encoding of arbitrary
  302. characters.
  303. @par Exception Safety
  304. Strong guarantee.
  305. @param u An object that derives from @ref url_base.
  306. @param fmt The format URL string.
  307. @param args Arguments to be formatted.
  308. @throws system_error
  309. `fmt` contains an invalid format string and
  310. `u` contains an invalid URL after replacements
  311. are applied.
  312. @par BNF
  313. @code
  314. replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
  315. arg_id ::= integer | identifier
  316. integer ::= digit+
  317. digit ::= "0"..."9"
  318. identifier ::= id_start id_continue*
  319. id_start ::= "a"..."z" | "A"..."Z" | "_"
  320. id_continue ::= id_start | digit
  321. @endcode
  322. @par Specification
  323. @li <a href="https://fmt.dev/latest/syntax.html"
  324. >Format String Syntax</a>
  325. @see
  326. @ref format.
  327. */
  328. inline
  329. void
  330. format_to(
  331. url_base& u,
  332. core::string_view fmt,
  333. std::initializer_list<format_arg> args)
  334. {
  335. detail::vformat_to(
  336. u, fmt, detail::format_args(
  337. args.begin(), args.end()));
  338. }
  339. /** Designate a named argument for a replacement field
  340. Construct a named argument for a format URL
  341. string that contains named replacement fields.
  342. The function parameters should be convertible
  343. to an implementation defined type able to
  344. store the name and a reference to any type
  345. potentially used as a format argument.
  346. @par Example
  347. The function should be used to designate a named
  348. argument for a replacement field in a format
  349. URL string.
  350. @code
  351. assert(format("user/{id}", arg("id", 1)).buffer() == "user/1");
  352. @endcode
  353. @return A temporary object with reference
  354. semantics for a named argument
  355. @param name The format argument name
  356. @param arg The format argument value
  357. @see
  358. @ref format,
  359. @ref format_to.
  360. */
  361. template <class T>
  362. named_arg<T>
  363. arg(core::string_view name, T const& arg)
  364. {
  365. return {name, arg};
  366. }
  367. } // url
  368. } // boost
  369. #endif