apply clang-tidy modernize-use-override
[folly.git] / folly / FormatArg.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <stdexcept>
20 #include <folly/Conv.h>
21 #include <folly/Likely.h>
22 #include <folly/Portability.h>
23 #include <folly/Range.h>
24
25 namespace folly {
26
27 class BadFormatArg : public std::invalid_argument {
28  public:
29   explicit BadFormatArg(const std::string& msg)
30     : std::invalid_argument(msg) {}
31 };
32
33 /**
34  * Parsed format argument.
35  */
36 struct FormatArg {
37   /**
38    * Parse a format argument from a string.  Keeps a reference to the
39    * passed-in string -- does not copy the given characters.
40    */
41   explicit FormatArg(StringPiece sp)
42     : fullArgString(sp),
43       fill(kDefaultFill),
44       align(Align::DEFAULT),
45       sign(Sign::DEFAULT),
46       basePrefix(false),
47       thousandsSeparator(false),
48       trailingDot(false),
49       width(kDefaultWidth),
50       widthIndex(kNoIndex),
51       precision(kDefaultPrecision),
52       presentation(kDefaultPresentation),
53       nextKeyMode_(NextKeyMode::NONE) {
54     if (!sp.empty()) {
55       initSlow();
56     }
57   }
58
59   enum class Type {
60     INTEGER,
61     FLOAT,
62     OTHER
63   };
64   /**
65    * Validate the argument for the given type; throws on error.
66    */
67   void validate(Type type) const;
68
69   /**
70    * Throw an exception if the first argument is false.  The exception
71    * message will contain the argument string as well as any passed-in
72    * arguments to enforce, formatted using folly::to<std::string>.
73    */
74   template <typename... Args>
75   void enforce(bool v, Args&&... args) const {
76     if (UNLIKELY(!v)) {
77       error(std::forward<Args>(args)...);
78     }
79   }
80
81   template <typename... Args>
82   std::string errorStr(Args&&... args) const;
83   template <typename... Args>
84   [[noreturn]] void error(Args&&... args) const;
85
86   /**
87    * Full argument string, as passed in to the constructor.
88    */
89   StringPiece fullArgString;
90
91   /**
92    * Fill
93    */
94   static constexpr char kDefaultFill = '\0';
95   char fill;
96
97   /**
98    * Alignment
99    */
100   enum class Align : uint8_t {
101     DEFAULT,
102     LEFT,
103     RIGHT,
104     PAD_AFTER_SIGN,
105     CENTER,
106     INVALID
107   };
108   Align align;
109
110   /**
111    * Sign
112    */
113   enum class Sign : uint8_t {
114     DEFAULT,
115     PLUS_OR_MINUS,
116     MINUS,
117     SPACE_OR_MINUS,
118     INVALID
119   };
120   Sign sign;
121
122   /**
123    * Output base prefix (0 for octal, 0x for hex)
124    */
125   bool basePrefix;
126
127   /**
128    * Output thousands separator (comma)
129    */
130   bool thousandsSeparator;
131
132   /**
133    * Force a trailing decimal on doubles which could be rendered as ints
134    */
135   bool trailingDot;
136
137   /**
138    * Field width and optional argument index
139    */
140   static constexpr int kDefaultWidth = -1;
141   static constexpr int kDynamicWidth = -2;
142   static constexpr int kNoIndex = -1;
143   int width;
144   int widthIndex;
145
146   /**
147    * Precision
148    */
149   static constexpr int kDefaultPrecision = -1;
150   int precision;
151
152   /**
153    * Presentation
154    */
155   static constexpr char kDefaultPresentation = '\0';
156   char presentation;
157
158   /**
159    * Split a key component from "key", which must be non-empty (an exception
160    * is thrown otherwise).
161    */
162   template <bool emptyOk=false>
163   StringPiece splitKey();
164
165   /**
166    * Is the entire key empty?
167    */
168   bool keyEmpty() const {
169     return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
170   }
171
172   /**
173    * Split an key component from "key", which must be non-empty and a valid
174    * integer (an exception is thrown otherwise).
175    */
176   int splitIntKey();
177
178   void setNextIntKey(int val) {
179     assert(nextKeyMode_ == NextKeyMode::NONE);
180     nextKeyMode_ = NextKeyMode::INT;
181     nextIntKey_ = val;
182   }
183
184   void setNextKey(StringPiece val) {
185     assert(nextKeyMode_ == NextKeyMode::NONE);
186     nextKeyMode_ = NextKeyMode::STRING;
187     nextKey_ = val;
188   }
189
190  private:
191   void initSlow();
192   template <bool emptyOk>
193   StringPiece doSplitKey();
194
195   StringPiece key_;
196   int nextIntKey_;
197   StringPiece nextKey_;
198   enum class NextKeyMode {
199     NONE,
200     INT,
201     STRING,
202   };
203   NextKeyMode nextKeyMode_;
204 };
205
206 template <typename... Args>
207 inline std::string FormatArg::errorStr(Args&&... args) const {
208   return to<std::string>(
209     "invalid format argument {", fullArgString, "}: ",
210     std::forward<Args>(args)...);
211 }
212
213 template <typename... Args>
214 [[noreturn]] inline void FormatArg::error(Args&&... args) const {
215   throw BadFormatArg(errorStr(std::forward<Args>(args)...));
216 }
217
218 template <bool emptyOk>
219 inline StringPiece FormatArg::splitKey() {
220   enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
221   return doSplitKey<emptyOk>();
222 }
223
224 template <bool emptyOk>
225 inline StringPiece FormatArg::doSplitKey() {
226   if (nextKeyMode_ == NextKeyMode::STRING) {
227     nextKeyMode_ = NextKeyMode::NONE;
228     if (!emptyOk) {  // static
229       enforce(!nextKey_.empty(), "non-empty key required");
230     }
231     return nextKey_;
232   }
233
234   if (key_.empty()) {
235     if (!emptyOk) {  // static
236       error("non-empty key required");
237     }
238     return StringPiece();
239   }
240
241   const char* b = key_.begin();
242   const char* e = key_.end();
243   const char* p;
244   if (e[-1] == ']') {
245     --e;
246     p = static_cast<const char*>(memchr(b, '[', size_t(e - b)));
247     enforce(p != nullptr, "unmatched ']'");
248   } else {
249     p = static_cast<const char*>(memchr(b, '.', size_t(e - b)));
250   }
251   if (p) {
252     key_.assign(p + 1, e);
253   } else {
254     p = e;
255     key_.clear();
256   }
257   if (!emptyOk) {  // static
258     enforce(b != p, "non-empty key required");
259   }
260   return StringPiece(b, p);
261 }
262
263 inline int FormatArg::splitIntKey() {
264   if (nextKeyMode_ == NextKeyMode::INT) {
265     nextKeyMode_ = NextKeyMode::NONE;
266     return nextIntKey_;
267   }
268   try {
269     return to<int>(doSplitKey<true>());
270   } catch (const std::out_of_range&) {
271     error("integer key required");
272     return 0;  // unreached
273   }
274 }
275
276 }  // namespace folly