blob: f4ebe0e343309ea0ca1aac8ff794ca7d4c8d79e5 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001//
2// Copyright 2019 The Abseil Authors.
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// https://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#include "absl/flags/marshalling.h"
17
18#include <limits>
19#include <type_traits>
20
21#include "absl/base/macros.h"
22#include "absl/strings/match.h"
23#include "absl/strings/numbers.h"
24#include "absl/strings/str_cat.h"
25#include "absl/strings/str_format.h"
26#include "absl/strings/str_join.h"
27#include "absl/strings/str_split.h"
28
29namespace absl {
30namespace flags_internal {
31
32// --------------------------------------------------------------------
33// AbslParseFlag specializations for boolean type.
34
35bool AbslParseFlag(absl::string_view text, bool* dst, std::string*) {
36 const char* kTrue[] = {"1", "t", "true", "y", "yes"};
37 const char* kFalse[] = {"0", "f", "false", "n", "no"};
38 static_assert(sizeof(kTrue) == sizeof(kFalse), "true_false_equal");
39
40 text = absl::StripAsciiWhitespace(text);
41
42 for (size_t i = 0; i < ABSL_ARRAYSIZE(kTrue); ++i) {
43 if (absl::EqualsIgnoreCase(text, kTrue[i])) {
44 *dst = true;
45 return true;
46 } else if (absl::EqualsIgnoreCase(text, kFalse[i])) {
47 *dst = false;
48 return true;
49 }
50 }
51 return false; // didn't match a legal input
52}
53
54// --------------------------------------------------------------------
55// AbslParseFlag for integral types.
56
57// Return the base to use for parsing text as an integer. Leading 0x
58// puts us in base 16. But leading 0 does not put us in base 8. It
59// caused too many bugs when we had that behavior.
60static int NumericBase(absl::string_view text) {
61 const bool hex = (text.size() >= 2 && text[0] == '0' &&
62 (text[1] == 'x' || text[1] == 'X'));
63 return hex ? 16 : 10;
64}
65
66template <typename IntType>
67inline bool ParseFlagImpl(absl::string_view text, IntType* dst) {
68 text = absl::StripAsciiWhitespace(text);
69
70 return absl::numbers_internal::safe_strtoi_base(text, dst, NumericBase(text));
71}
72
73bool AbslParseFlag(absl::string_view text, short* dst, std::string*) {
74 int val;
75 if (!ParseFlagImpl(text, &val)) return false;
76 if (static_cast<short>(val) != val) // worked, but number out of range
77 return false;
78 *dst = static_cast<short>(val);
79 return true;
80}
81
82bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) {
83 unsigned int val;
84 if (!ParseFlagImpl(text, &val)) return false;
85 if (static_cast<unsigned short>(val) !=
86 val) // worked, but number out of range
87 return false;
88 *dst = static_cast<unsigned short>(val);
89 return true;
90}
91
92bool AbslParseFlag(absl::string_view text, int* dst, std::string*) {
93 return ParseFlagImpl(text, dst);
94}
95
96bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) {
97 return ParseFlagImpl(text, dst);
98}
99
100bool AbslParseFlag(absl::string_view text, long* dst, std::string*) {
101 return ParseFlagImpl(text, dst);
102}
103
104bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) {
105 return ParseFlagImpl(text, dst);
106}
107
108bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) {
109 return ParseFlagImpl(text, dst);
110}
111
112bool AbslParseFlag(absl::string_view text, unsigned long long* dst,
113 std::string*) {
114 return ParseFlagImpl(text, dst);
115}
116
117// --------------------------------------------------------------------
118// AbslParseFlag for floating point types.
119
120bool AbslParseFlag(absl::string_view text, float* dst, std::string*) {
121 return absl::SimpleAtof(text, dst);
122}
123
124bool AbslParseFlag(absl::string_view text, double* dst, std::string*) {
125 return absl::SimpleAtod(text, dst);
126}
127
128// --------------------------------------------------------------------
129// AbslParseFlag for strings.
130
131bool AbslParseFlag(absl::string_view text, std::string* dst, std::string*) {
132 dst->assign(text.data(), text.size());
133 return true;
134}
135
136// --------------------------------------------------------------------
137// AbslParseFlag for vector of strings.
138
139bool AbslParseFlag(absl::string_view text, std::vector<std::string>* dst,
140 std::string*) {
141 // An empty flag value corresponds to an empty vector, not a vector
142 // with a single, empty std::string.
143 if (text.empty()) {
144 dst->clear();
145 return true;
146 }
147 *dst = absl::StrSplit(text, ',', absl::AllowEmpty());
148 return true;
149}
150
151// --------------------------------------------------------------------
152// AbslUnparseFlag specializations for various builtin flag types.
153
154std::string Unparse(bool v) { return v ? "true" : "false"; }
155std::string Unparse(short v) { return absl::StrCat(v); }
156std::string Unparse(unsigned short v) { return absl::StrCat(v); }
157std::string Unparse(int v) { return absl::StrCat(v); }
158std::string Unparse(unsigned int v) { return absl::StrCat(v); }
159std::string Unparse(long v) { return absl::StrCat(v); }
160std::string Unparse(unsigned long v) { return absl::StrCat(v); }
161std::string Unparse(long long v) { return absl::StrCat(v); }
162std::string Unparse(unsigned long long v) { return absl::StrCat(v); }
163template <typename T>
164std::string UnparseFloatingPointVal(T v) {
165 // digits10 is guaranteed to roundtrip correctly in std::string -> value -> std::string
166 // conversions, but may not be enough to represent all the values correctly.
167 std::string digit10_str =
168 absl::StrFormat("%.*g", std::numeric_limits<T>::digits10, v);
169 if (std::isnan(v) || std::isinf(v)) return digit10_str;
170
171 T roundtrip_val = 0;
172 std::string err;
173 if (absl::ParseFlag(digit10_str, &roundtrip_val, &err) &&
174 roundtrip_val == v) {
175 return digit10_str;
176 }
177
178 // max_digits10 is the number of base-10 digits that are necessary to uniquely
179 // represent all distinct values.
180 return absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10, v);
181}
182std::string Unparse(float v) { return UnparseFloatingPointVal(v); }
183std::string Unparse(double v) { return UnparseFloatingPointVal(v); }
184std::string AbslUnparseFlag(absl::string_view v) { return std::string(v); }
185std::string AbslUnparseFlag(const std::vector<std::string>& v) {
186 return absl::StrJoin(v, ",");
187}
188
189} // namespace flags_internal
190
191bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst,
192 std::string* err) {
193 text = absl::StripAsciiWhitespace(text);
194 if (text.empty()) {
195 *err = "no value provided";
196 return false;
197 }
198 if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1);
199 if (absl::EqualsIgnoreCase(text, "info")) {
200 *dst = absl::LogSeverity::kInfo;
201 return true;
202 }
203 if (absl::EqualsIgnoreCase(text, "warning")) {
204 *dst = absl::LogSeverity::kWarning;
205 return true;
206 }
207 if (absl::EqualsIgnoreCase(text, "error")) {
208 *dst = absl::LogSeverity::kError;
209 return true;
210 }
211 if (absl::EqualsIgnoreCase(text, "fatal")) {
212 *dst = absl::LogSeverity::kFatal;
213 return true;
214 }
215 std::underlying_type<absl::LogSeverity>::type numeric_value;
216 if (absl::ParseFlag(text, &numeric_value, err)) {
217 *dst = static_cast<absl::LogSeverity>(numeric_value);
218 return true;
219 }
220 *err = "only integers and absl::LogSeverity enumerators are accepted";
221 return false;
222}
223
224std::string AbslUnparseFlag(absl::LogSeverity v) {
225 if (v == absl::NormalizeLogSeverity(v)) return absl::LogSeverityName(v);
226 return absl::UnparseFlag(static_cast<int>(v));
227}
228
229} // namespace absl