/src/trafficserver/lib/swoc/include/swoc/bwf_ex.h
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // Copyright Apache Software Foundation 2019 |
3 | | /** @file |
4 | | |
5 | | BufferWriter formatters for types in the std namespace. |
6 | | */ |
7 | | |
8 | | #pragma once |
9 | | |
10 | | #include <array> |
11 | | #include <string_view> |
12 | | |
13 | | #include "swoc/swoc_version.h" |
14 | | #include "swoc/bwf_base.h" |
15 | | #include "swoc/swoc_meta.h" |
16 | | |
17 | | namespace swoc { inline namespace SWOC_VERSION_NS { |
18 | | namespace bwf { |
19 | | using namespace swoc::literals; // enable ""sv |
20 | | |
21 | | /** Output @a text @a n times. |
22 | | * |
23 | | */ |
24 | | struct Pattern { |
25 | | int _n; ///< # of instances of @a pattern. |
26 | | std::string_view _text; ///< output text. |
27 | | }; |
28 | | |
29 | | /** Format wrapper for @c errno. |
30 | | * This stores a copy of the argument or @c errno if an argument isn't provided. The output |
31 | | * is then formatted with the short, long, and numeric value of @c errno. If the format specifier |
32 | | * is type 'd' then just the numeric value is printed. |
33 | | */ |
34 | | struct Errno { |
35 | | int _e; ///< Errno value. |
36 | | |
37 | | /// Construct wrapper, default to current @c errno |
38 | 0 | explicit Errno(int e = errno) : _e(e) {} |
39 | | }; |
40 | | |
41 | | /** Format wrapper for time stamps. |
42 | | * If the time isn't provided, the current epoch time is used. If the format string isn't |
43 | | * provided a format like "2017 Jun 29 14:11:29" is used. |
44 | | */ |
45 | | struct Date { |
46 | | /// Default format |
47 | | static constexpr std::string_view DEFAULT_FORMAT{"%Y %b %d %H:%M:%S"_sv}; |
48 | | time_t _epoch; ///< The time. |
49 | | std::string_view _fmt; ///< Data format. |
50 | | |
51 | | /** Constructor. |
52 | | * |
53 | | * @param t The timestamp. |
54 | | * @param fmt Timestamp format. |
55 | | */ |
56 | 0 | Date(time_t t, std::string_view fmt = DEFAULT_FORMAT) : _epoch(t), _fmt(fmt) {} |
57 | | |
58 | | /// Default construct using current time with optional format. |
59 | | Date(std::string_view fmt = DEFAULT_FORMAT); |
60 | | }; |
61 | | |
62 | | namespace detail { |
63 | | // Special case conversions - these handle nullptr because the @c std::string_view spec is stupid. |
64 | | inline std::string_view |
65 | 0 | FirstOfConverter(std::nullptr_t) { |
66 | 0 | return std::string_view{}; |
67 | 0 | } |
68 | | |
69 | | inline std::string_view |
70 | 0 | FirstOfConverter(char const *s) { |
71 | 0 | return std::string_view{s ? s : ""}; |
72 | 0 | } |
73 | | |
74 | | // Otherwise do any compliant conversion. |
75 | | template <typename T> |
76 | | std::string_view |
77 | | FirstOfConverter(T &&t) { |
78 | | return t; |
79 | | } |
80 | | } // namespace detail |
81 | | |
82 | | /// Print the first of a list of strings that is not an empty string. |
83 | | /// All arguments must be convertible to @c std::string_view. |
84 | | template <typename... Args> |
85 | | std::string_view |
86 | | FirstOf(Args &&...args) { |
87 | | std::array<std::string_view, sizeof...(args)> strings{{detail::FirstOfConverter(args)...}}; |
88 | | for (auto &s : strings) { |
89 | | if (!s.empty()) |
90 | | return s; |
91 | | } |
92 | | return std::string_view{}; |
93 | | } |
94 | | |
95 | | /** Wrapper for a sub-text, where the @a args are output according to @a fmt. |
96 | | * |
97 | | * @tparam Args Argument types. |
98 | | */ |
99 | | template <typename... Args> struct SubText { |
100 | | using arg_pack = std::tuple<Args...>; ///< The pack of arguments for format string. |
101 | | TextView _fmt; ///< Format string. If empty, do not generate output. |
102 | | arg_pack _args; ///< Arguments to format string. |
103 | | |
104 | | /// Construct with a specific @a fmt and @a args. |
105 | 0 | SubText(TextView const &fmt, arg_pack const &args) : _fmt(fmt), _args(args){};Unexecuted instantiation: swoc::_1_5_15::bwf::SubText<char const* const&>::SubText(swoc::_1_5_15::TextView const&, std::__1::tuple<char const* const&> const&) Unexecuted instantiation: swoc::_1_5_15::bwf::SubText<swoc::_1_5_15::TextView&>::SubText(swoc::_1_5_15::TextView const&, std::__1::tuple<swoc::_1_5_15::TextView&> const&) Unexecuted instantiation: swoc::_1_5_15::bwf::SubText<swoc::_1_5_15::Errata::Severity, swoc::_1_5_15::TextView&>::SubText(swoc::_1_5_15::TextView const&, std::__1::tuple<swoc::_1_5_15::Errata::Severity, swoc::_1_5_15::TextView&> const&) |
106 | | |
107 | | /// Check for output not enabled. |
108 | | bool operator!() const; |
109 | | |
110 | | /// Check for output enabled. |
111 | | explicit operator bool() const; |
112 | | }; |
113 | | |
114 | | template <typename... Args> SubText<Args...>::operator bool() const { |
115 | | return !_fmt.empty(); |
116 | | } |
117 | | |
118 | | template <typename... Args> |
119 | | bool |
120 | | SubText<Args...>::operator!() const { |
121 | | return _fmt.empty(); |
122 | | } |
123 | | |
124 | | /** Optional printing wrapper. |
125 | | * |
126 | | * @tparam Args Arguments for output. |
127 | | * @param flag Generate output flag. |
128 | | * @param fmt Format for output and args. |
129 | | * @param args The arguments. |
130 | | * @return A wrapper for the optional text. |
131 | | * |
132 | | * This function is passed a @a flag, a printing format @a fmt, and a set of arguments @a args to |
133 | | * be used by the format. Output is generated if @a flag is @c true, otherwise the empty string |
134 | | * (no output) is generated. For example, if in a function there was a flag to determine if an |
135 | | * extra tag with delimiters, e.g. "[tag]", was to be generated, this could be done with |
136 | | * |
137 | | * @code |
138 | | * w.print("Some other text{}.", bwf::If(flag, " [{}]", tag)); |
139 | | * @endcode |
140 | | * |
141 | | * @internal To disambiguate overloads, this is enabled only if there is at least one argument |
142 | | * to be passed to the format string. |
143 | | */ |
144 | | template <typename... Args> |
145 | | SubText<Args...> |
146 | 0 | If(bool flag, TextView const &fmt, Args &&...args) { |
147 | 0 | return SubText<Args...>(flag ? fmt : TextView{}, std::forward_as_tuple(args...)); |
148 | 0 | } Unexecuted instantiation: swoc::_1_5_15::bwf::SubText<char const* const&> swoc::_1_5_15::bwf::If<char const* const&>(bool, swoc::_1_5_15::TextView const&, char const* const&) Unexecuted instantiation: swoc::_1_5_15::bwf::SubText<swoc::_1_5_15::TextView&> swoc::_1_5_15::bwf::If<swoc::_1_5_15::TextView&>(bool, swoc::_1_5_15::TextView const&, swoc::_1_5_15::TextView&) Unexecuted instantiation: swoc::_1_5_15::bwf::SubText<swoc::_1_5_15::Errata::Severity, swoc::_1_5_15::TextView&> swoc::_1_5_15::bwf::If<swoc::_1_5_15::Errata::Severity, swoc::_1_5_15::TextView&>(bool, swoc::_1_5_15::TextView const&, swoc::_1_5_15::Errata::Severity&&, swoc::_1_5_15::TextView&) |
149 | | |
150 | | namespace detail { |
151 | | // @a T has the @c empty() method. |
152 | | template <typename T> |
153 | | auto |
154 | | Optional(meta::CaseTag<2>, TextView fmt, T &&t) -> decltype(void(t.empty()), meta::TypeFunc<SubText<T>>()) { |
155 | | return SubText<T>(t.empty() ? TextView{} : fmt, std::forward_as_tuple(t)); |
156 | | } |
157 | | |
158 | | // @a T is convertible to @c bool. |
159 | | template <typename T> |
160 | | auto |
161 | | Optional(meta::CaseTag<1>, TextView fmt, T &&t) -> decltype(bool(t), meta::TypeFunc<SubText<T>>()) { |
162 | | return SubText<T>(bool(t) ? fmt : TextView{}, std::forward_as_tuple(t)); |
163 | | } |
164 | | |
165 | | // @a T is not optional, always print. |
166 | | template <typename T> |
167 | | auto |
168 | | Optional(meta::CaseTag<0>, TextView fmt, T &&t) -> SubText<T> { |
169 | | return SubText<T>(fmt, std::forward_as_tuple(t)); |
170 | | } |
171 | | } // namespace detail |
172 | | |
173 | | /** Simplified optional text wrapper. |
174 | | * |
175 | | * @tparam ARG the type of the (single) argument. |
176 | | * @param fmt Format string. |
177 | | * @param arg The single argument to the format string and predicate. |
178 | | * @return An optional text wrapper. |
179 | | * |
180 | | * This generates output iff @a arg is not empty. @a fmt is required to take only a single |
181 | | * argument, which will be @a arg. This is a convenience overload, to handle the common case |
182 | | * where the argument and the conditional are the same. The argument must have one of the |
183 | | * following properties in order to serve as the conditional. These are checked in order. |
184 | | * |
185 | | * - The @c empty() method which returns @c true if the argument is empty and should not be printed. |
186 | | * This handles the case of C++ string types. |
187 | | * |
188 | | * - Conversion to @c bool which is @c false if the argument should not be printed. This covers the |
189 | | * case of pointers. |
190 | | * |
191 | | * As an example, if an output function had three strings @a alpha, @a bravo, and |
192 | | * @a charlie, each of which could be null, which should be output with space separators, |
193 | | * this would be |
194 | | * @code |
195 | | * w.print("Leading text{}{}{}.", Optional(" {}", alpha) |
196 | | * , Optional(" {}", bravo) |
197 | | * , Optional(" {}", charlie)); |
198 | | * @endcode |
199 | | * |
200 | | * Because of the property handling, these strings can be C styles strings ( @c char* ) or C++ |
201 | | * string types (such as @c std::string_view ). |
202 | | * |
203 | | */ |
204 | | template <typename ARG> |
205 | | SubText<ARG> |
206 | | Optional(TextView fmt, ARG &&arg) { |
207 | | return detail::Optional(meta::CaseArg, fmt, std::forward<ARG>(arg)); |
208 | | } |
209 | | |
210 | | /** Convert from ASCII hexadecimal to raw bytes. |
211 | | * |
212 | | * E.g. if the source span contains "4576696c20446176652052756c7a" then "Evil Dave Rulz" is the output. |
213 | | * For format specifier support, on lhe max width is used. Any @c MemSpan compatible class can be used |
214 | | * as the target, including @c std::string and @c std::string_view. |
215 | | * |
216 | | * @code |
217 | | * void f(std::string const& str) { |
218 | | * w.print("{}", bwf::UnHex(str)); |
219 | | * // ... |
220 | | * @endcode |
221 | | */ |
222 | | struct UnHex { |
223 | 0 | UnHex(MemSpan<void const> const &span) : _span(span) {} |
224 | | MemSpan<void const> _span; ///< Source span. |
225 | | }; |
226 | | } // namespace bwf |
227 | | |
228 | | /** Repeatedly output a pattern. |
229 | | * |
230 | | * @param w Output. |
231 | | * @param spec Format specifier. |
232 | | * @param pattern Output patterning. |
233 | | * @return @a w |
234 | | * |
235 | | * The @a pattern contains the count and text to output. |
236 | | */ |
237 | | BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Pattern const &pattern); |
238 | | |
239 | | /** Format an integer as an @c errno value. |
240 | | * |
241 | | * @param w Output. |
242 | | * @param spec Format specifier. |
243 | | * @param e Error code. |
244 | | * @return @a w |
245 | | */ |
246 | | BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Errno const &e); |
247 | | |
248 | | /** Format a timestamp wrapped in a @c Date. |
249 | | * |
250 | | * @param w Output. |
251 | | * @param spec Format specifier. |
252 | | * @param date Timestamp. |
253 | | * @return @a w |
254 | | */ |
255 | | BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Date const &date); |
256 | | |
257 | | BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::UnHex const &obj); |
258 | | |
259 | | /** Output a nested formatted string. |
260 | | * |
261 | | * @tparam Args Argument pack for @a subtext. |
262 | | * @param w Output |
263 | | * @param subtext Format string and arguments. |
264 | | * @return @a w |
265 | | * |
266 | | * This supports a nested format string and arguments inside another format string. This is most often useful |
267 | | * if one of the formats is fixed or pre-compiled. |
268 | | * |
269 | | * @code |
270 | | * bwformat(w, "Line {} offset {} with data {}.", line_no, line_off, bwf::SubText("alpha {} bravo {}", alpha, bravo")); |
271 | | * @endcode |
272 | | * |
273 | | * @see bwf::Subtext |
274 | | */ |
275 | | template <typename... Args> |
276 | | BufferWriter & |
277 | 0 | bwformat(BufferWriter &w, bwf::Spec const &, bwf::SubText<Args...> const &subtext) { |
278 | 0 | if (!subtext._fmt.empty()) { |
279 | 0 | w.print_v(subtext._fmt, subtext._args); |
280 | 0 | } |
281 | 0 | return w; |
282 | 0 | } Unexecuted instantiation: swoc::_1_5_15::BufferWriter& swoc::_1_5_15::bwformat<char const* const&>(swoc::_1_5_15::BufferWriter&, swoc::_1_5_15::bwf::Spec const&, swoc::_1_5_15::bwf::SubText<char const* const&> const&) Unexecuted instantiation: swoc::_1_5_15::BufferWriter& swoc::_1_5_15::bwformat<swoc::_1_5_15::TextView&>(swoc::_1_5_15::BufferWriter&, swoc::_1_5_15::bwf::Spec const&, swoc::_1_5_15::bwf::SubText<swoc::_1_5_15::TextView&> const&) Unexecuted instantiation: swoc::_1_5_15::BufferWriter& swoc::_1_5_15::bwformat<swoc::_1_5_15::Errata::Severity, swoc::_1_5_15::TextView&>(swoc::_1_5_15::BufferWriter&, swoc::_1_5_15::bwf::Spec const&, swoc::_1_5_15::bwf::SubText<swoc::_1_5_15::Errata::Severity, swoc::_1_5_15::TextView&> const&) |
283 | | |
284 | | }} // namespace swoc::SWOC_VERSION_NS |