format.hpp 11 KB

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