2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <folly/Conv.h>
21 #include <folly/Likely.h>
22 #include <folly/Portability.h>
23 #include <folly/Range.h>
27 class BadFormatArg : public std::invalid_argument {
29 explicit BadFormatArg(const std::string& msg)
30 : std::invalid_argument(msg) {}
34 * Parsed format argument.
38 * Parse a format argument from a string. Keeps a reference to the
39 * passed-in string -- does not copy the given characters.
41 explicit FormatArg(StringPiece sp)
44 align(Align::DEFAULT),
47 thousandsSeparator(false),
51 precision(kDefaultPrecision),
52 presentation(kDefaultPresentation),
53 nextKeyMode_(NextKeyMode::NONE) {
65 * Validate the argument for the given type; throws on error.
67 void validate(Type type) const;
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>.
74 template <typename... Args>
75 void enforce(bool v, Args&&... args) const {
77 error(std::forward<Args>(args)...);
81 template <typename... Args>
82 std::string errorStr(Args&&... args) const;
83 template <typename... Args>
84 [[noreturn]] void error(Args&&... args) const;
87 * Full argument string, as passed in to the constructor.
89 StringPiece fullArgString;
94 static constexpr char kDefaultFill = '\0';
100 enum class Align : uint8_t {
113 enum class Sign : uint8_t {
123 * Output base prefix (0 for octal, 0x for hex)
128 * Output thousands separator (comma)
130 bool thousandsSeparator;
133 * Force a trailing decimal on doubles which could be rendered as ints
138 * Field width and optional argument index
140 static constexpr int kDefaultWidth = -1;
141 static constexpr int kDynamicWidth = -2;
142 static constexpr int kNoIndex = -1;
149 static constexpr int kDefaultPrecision = -1;
155 static constexpr char kDefaultPresentation = '\0';
159 * Split a key component from "key", which must be non-empty (an exception
160 * is thrown otherwise).
162 template <bool emptyOk=false>
163 StringPiece splitKey();
166 * Is the entire key empty?
168 bool keyEmpty() const {
169 return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
173 * Split an key component from "key", which must be non-empty and a valid
174 * integer (an exception is thrown otherwise).
178 void setNextIntKey(int val) {
179 assert(nextKeyMode_ == NextKeyMode::NONE);
180 nextKeyMode_ = NextKeyMode::INT;
184 void setNextKey(StringPiece val) {
185 assert(nextKeyMode_ == NextKeyMode::NONE);
186 nextKeyMode_ = NextKeyMode::STRING;
192 template <bool emptyOk>
193 StringPiece doSplitKey();
197 StringPiece nextKey_;
198 enum class NextKeyMode {
203 NextKeyMode nextKeyMode_;
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)...);
213 template <typename... Args>
214 inline void FormatArg::error(Args&&... args) const {
215 throw BadFormatArg(errorStr(std::forward<Args>(args)...));
218 template <bool emptyOk>
219 inline StringPiece FormatArg::splitKey() {
220 enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
221 return doSplitKey<emptyOk>();
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");
235 if (!emptyOk) { // static
236 error("non-empty key required");
238 return StringPiece();
241 const char* b = key_.begin();
242 const char* e = key_.end();
246 p = static_cast<const char*>(memchr(b, '[', e - b));
247 enforce(p, "unmatched ']'");
249 p = static_cast<const char*>(memchr(b, '.', e - b));
252 key_.assign(p + 1, e);
257 if (!emptyOk) { // static
258 enforce(b != p, "non-empty key required");
260 return StringPiece(b, p);
263 inline int FormatArg::splitIntKey() {
264 if (nextKeyMode_ == NextKeyMode::INT) {
265 nextKeyMode_ = NextKeyMode::NONE;
269 return to<int>(doSplitKey<true>());
270 } catch (const std::out_of_range& e) {
271 error("integer key required");
272 return 0; // unreached