2 * Copyright 2012-present 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.
21 #include <folly/CPortability.h>
22 #include <folly/Conv.h>
23 #include <folly/Likely.h>
24 #include <folly/Portability.h>
25 #include <folly/Range.h>
29 class FOLLY_EXPORT BadFormatArg : public std::invalid_argument {
30 using invalid_argument::invalid_argument;
33 [[noreturn]] void throwBadFormatArg(char const* msg);
34 [[noreturn]] void throwBadFormatArg(std::string const& msg);
37 * Parsed format argument.
41 * Parse a format argument from a string. Keeps a reference to the
42 * passed-in string -- does not copy the given characters.
44 explicit FormatArg(StringPiece sp)
47 align(Align::DEFAULT),
50 thousandsSeparator(false),
54 precision(kDefaultPrecision),
55 presentation(kDefaultPresentation),
56 nextKeyMode_(NextKeyMode::NONE) {
68 * Validate the argument for the given type; throws on error.
70 void validate(Type type) const;
73 * Throw an exception if the first argument is false. The exception
74 * message will contain the argument string as well as any passed-in
75 * arguments to enforce, formatted using folly::to<std::string>.
77 template <typename... Args>
78 void enforce(bool v, Args&&... args) const {
80 error(std::forward<Args>(args)...);
84 template <typename... Args>
85 std::string errorStr(Args&&... args) const;
86 template <typename... Args>
87 [[noreturn]] void error(Args&&... args) const;
90 * Full argument string, as passed in to the constructor.
92 StringPiece fullArgString;
97 static constexpr char kDefaultFill = '\0';
103 enum class Align : uint8_t {
116 enum class Sign : uint8_t {
126 * Output base prefix (0 for octal, 0x for hex)
131 * Output thousands separator (comma)
133 bool thousandsSeparator;
136 * Force a trailing decimal on doubles which could be rendered as ints
141 * Field width and optional argument index
143 static constexpr int kDefaultWidth = -1;
144 static constexpr int kDynamicWidth = -2;
145 static constexpr int kNoIndex = -1;
152 static constexpr int kDefaultPrecision = -1;
158 static constexpr char kDefaultPresentation = '\0';
162 * Split a key component from "key", which must be non-empty (an exception
163 * is thrown otherwise).
165 template <bool emptyOk=false>
166 StringPiece splitKey();
169 * Is the entire key empty?
171 bool keyEmpty() const {
172 return nextKeyMode_ == NextKeyMode::NONE && key_.empty();
176 * Split an key component from "key", which must be non-empty and a valid
177 * integer (an exception is thrown otherwise).
181 void setNextIntKey(int val) {
182 assert(nextKeyMode_ == NextKeyMode::NONE);
183 nextKeyMode_ = NextKeyMode::INT;
187 void setNextKey(StringPiece val) {
188 assert(nextKeyMode_ == NextKeyMode::NONE);
189 nextKeyMode_ = NextKeyMode::STRING;
195 template <bool emptyOk>
196 StringPiece doSplitKey();
200 StringPiece nextKey_;
201 enum class NextKeyMode {
206 NextKeyMode nextKeyMode_;
209 template <typename... Args>
210 inline std::string FormatArg::errorStr(Args&&... args) const {
211 return to<std::string>(
212 "invalid format argument {", fullArgString, "}: ",
213 std::forward<Args>(args)...);
216 template <typename... Args>
217 [[noreturn]] inline void FormatArg::error(Args&&... args) const {
218 throwBadFormatArg(errorStr(std::forward<Args>(args)...));
221 template <bool emptyOk>
222 inline StringPiece FormatArg::splitKey() {
223 enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected");
224 return doSplitKey<emptyOk>();
227 template <bool emptyOk>
228 inline StringPiece FormatArg::doSplitKey() {
229 if (nextKeyMode_ == NextKeyMode::STRING) {
230 nextKeyMode_ = NextKeyMode::NONE;
231 if (!emptyOk) { // static
232 enforce(!nextKey_.empty(), "non-empty key required");
238 if (!emptyOk) { // static
239 error("non-empty key required");
241 return StringPiece();
244 const char* b = key_.begin();
245 const char* e = key_.end();
249 p = static_cast<const char*>(memchr(b, '[', size_t(e - b)));
250 enforce(p != nullptr, "unmatched ']'");
252 p = static_cast<const char*>(memchr(b, '.', size_t(e - b)));
255 key_.assign(p + 1, e);
260 if (!emptyOk) { // static
261 enforce(b != p, "non-empty key required");
263 return StringPiece(b, p);
266 inline int FormatArg::splitIntKey() {
267 if (nextKeyMode_ == NextKeyMode::INT) {
268 nextKeyMode_ = NextKeyMode::NONE;
272 return to<int>(doSplitKey<true>());
273 } catch (const std::out_of_range&) {
274 error("integer key required");
275 return 0; // unreached