Skip to content

Commit 28e7a8a

Browse files
committed
Separate //cuttlefish/result:error_type from //cuttlefish/result
No users of `Result` interact directly with these types. Bug: b/471062163
1 parent abe754a commit 28e7a8a

File tree

4 files changed

+279
-243
lines changed

4 files changed

+279
-243
lines changed

base/cvd/cuttlefish/result/BUILD.bazel

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@ package(
44
default_visibility = ["//:android_cuttlefish"],
55
)
66

7+
cf_cc_library(
8+
name = "error_type",
9+
srcs = ["error_type.cc"],
10+
hdrs = ["error_type.h"],
11+
deps = [
12+
"//libbase",
13+
"@fmt",
14+
],
15+
)
16+
717
cf_cc_library(
818
name = "result",
9-
srcs = ["result.cc"],
1019
hdrs = ["result.h"],
1120
deps = [
21+
"//cuttlefish/result:error_type",
1222
"//libbase",
1323
"@fmt",
1424
],
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
#include "cuttlefish/result/result.h"
16+
#include "cuttlefish/result/error_type.h"
1717

1818
#include <optional>
1919
#include <sstream>
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
//
2+
// Copyright (C) 2022 The Android Open Source Project
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+
#pragma once
17+
18+
#include <optional>
19+
#include <ostream>
20+
#include <sstream>
21+
#include <string>
22+
#include <type_traits>
23+
#include <utility>
24+
#include <vector>
25+
26+
#include <android-base/format.h> // IWYU pragma: export
27+
#include <android-base/logging.h>
28+
#include <android-base/result.h> // IWYU pragma: export
29+
30+
namespace cuttlefish {
31+
32+
class StackTraceError;
33+
34+
class StackTraceEntry {
35+
public:
36+
enum class FormatSpecifier : char {
37+
/** Prefix multi-line output with an arrow. */
38+
kArrow = 'a',
39+
/** Use colors in all other output specifiers. */
40+
kColor = 'c',
41+
/** The function name without namespace or arguments. */
42+
kFunction = 'f',
43+
/** The CF_EXPECT(exp) expression. */
44+
kLongExpression = 'E',
45+
/** The source file path relative to ANDROID_BUILD_TOP and line number. */
46+
kLongLocation = 'L',
47+
/** The user-friendly string provided to CF_EXPECT. */
48+
kMessage = 'm',
49+
/** Prefix output with the stack frame index. */
50+
kNumbers = 'n',
51+
/** The function signature with fully-qualified types. */
52+
kPrettyFunction = 'F',
53+
/** The short location and short filename. */
54+
kShort = 's',
55+
/** The `exp` inside `CF_EXPECT(exp)` */
56+
kShortExpression = 'e',
57+
/** The source file basename and line number. */
58+
kShortLocation = 'l',
59+
};
60+
static constexpr auto kVerbose = {
61+
FormatSpecifier::kArrow,
62+
FormatSpecifier::kColor,
63+
FormatSpecifier::kNumbers,
64+
FormatSpecifier::kShort,
65+
};
66+
static constexpr auto kVeryVerbose = {
67+
FormatSpecifier::kArrow, FormatSpecifier::kColor,
68+
FormatSpecifier::kNumbers, FormatSpecifier::kLongLocation,
69+
FormatSpecifier::kPrettyFunction, FormatSpecifier::kLongExpression,
70+
FormatSpecifier::kMessage,
71+
};
72+
73+
StackTraceEntry(std::string file, size_t line, std::string pretty_function,
74+
std::string function);
75+
76+
StackTraceEntry(std::string file, size_t line, std::string pretty_function,
77+
std::string function, std::string expression);
78+
79+
StackTraceEntry(const StackTraceEntry& other);
80+
81+
StackTraceEntry(StackTraceEntry&&) = default;
82+
StackTraceEntry& operator=(const StackTraceEntry& other);
83+
StackTraceEntry& operator=(StackTraceEntry&&) = default;
84+
85+
template <typename T>
86+
StackTraceEntry& operator<<(T&& message_ext) & {
87+
message_ << std::forward<T>(message_ext);
88+
return *this;
89+
}
90+
template <typename T>
91+
StackTraceEntry operator<<(T&& message_ext) && {
92+
message_ << std::forward<T>(message_ext);
93+
return std::move(*this);
94+
}
95+
96+
operator StackTraceError() &&;
97+
template <typename T>
98+
operator android::base::expected<T, StackTraceError>() &&;
99+
100+
bool HasMessage() const;
101+
102+
/*
103+
* Print a single stack trace entry out of a list of format specifiers.
104+
* Some format specifiers [a,c,n] cause changes that affect all lines, while
105+
* the rest amount to printing a single line in the output. This code is
106+
* reused by formatting code for both rendering individual stack trace
107+
* entries, and rendering an entire stack trace with multiple entries.
108+
*/
109+
fmt::format_context::iterator format(
110+
fmt::format_context& ctx, const std::vector<FormatSpecifier>& specifiers,
111+
std::optional<int> index) const;
112+
113+
private:
114+
std::string file_;
115+
size_t line_;
116+
std::string pretty_function_;
117+
std::string function_;
118+
std::string expression_;
119+
std::stringstream message_;
120+
};
121+
122+
std::string ResultErrorFormat(bool color);
123+
124+
#define CF_STACK_TRACE_ENTRY(expression) \
125+
StackTraceEntry(__FILE__, __LINE__, __PRETTY_FUNCTION__, __func__, expression)
126+
127+
} // namespace cuttlefish
128+
129+
/**
130+
* Specialized formatting for StackTraceEntry based on user-provided specifiers.
131+
*
132+
* A StackTraceEntry can be formatted with {:specifiers} in a `fmt::format`
133+
* string, where `specifiers` is an ordered list of characters deciding on the
134+
* format. `v` provides "verbose" output and `V` provides "very verbose" output.
135+
* See `StackTraceEntry::FormatSpecifiers` for more fine-grained specifiers.
136+
*/
137+
template <>
138+
struct fmt::formatter<cuttlefish::StackTraceEntry> {
139+
public:
140+
constexpr auto parse(format_parse_context& ctx)
141+
-> format_parse_context::iterator {
142+
auto it = ctx.begin();
143+
while (it != ctx.end() && *it != '}') {
144+
if (*it == 'v') {
145+
for (const auto& specifier : cuttlefish::StackTraceEntry::kVerbose) {
146+
fmt_specs_.push_back(specifier);
147+
}
148+
} else if (*it == 'V') {
149+
for (const auto& specifier :
150+
cuttlefish::StackTraceEntry::kVeryVerbose) {
151+
fmt_specs_.push_back(specifier);
152+
}
153+
} else {
154+
fmt_specs_.push_back(
155+
static_cast<cuttlefish::StackTraceEntry::FormatSpecifier>(*it));
156+
}
157+
it++;
158+
}
159+
return it;
160+
}
161+
162+
auto format(const cuttlefish::StackTraceEntry& entry,
163+
format_context& ctx) const -> format_context::iterator {
164+
return entry.format(ctx, fmt_specs_, std::nullopt);
165+
}
166+
167+
private:
168+
std::vector<cuttlefish::StackTraceEntry::FormatSpecifier> fmt_specs_;
169+
};
170+
171+
namespace cuttlefish {
172+
173+
class StackTraceError {
174+
public:
175+
StackTraceError& PushEntry(StackTraceEntry entry) & {
176+
stack_.emplace_back(std::move(entry));
177+
return *this;
178+
}
179+
StackTraceError PushEntry(StackTraceEntry entry) && {
180+
stack_.emplace_back(std::move(entry));
181+
return std::move(*this);
182+
}
183+
const std::vector<StackTraceEntry>& Stack() const { return stack_; }
184+
185+
std::string Message() const {
186+
return fmt::format(fmt::runtime("{:m}"), *this);
187+
}
188+
189+
std::string Trace() const { return fmt::format(fmt::runtime("{:v}"), *this); }
190+
191+
std::string FormatForEnv(bool color = (isatty(STDERR_FILENO) == 1)) const {
192+
return fmt::format(fmt::runtime(ResultErrorFormat(color)), *this);
193+
}
194+
195+
template <typename T>
196+
operator android::base::expected<T, StackTraceError>() && {
197+
return android::base::unexpected(std::move(*this));
198+
}
199+
200+
private:
201+
std::vector<StackTraceEntry> stack_;
202+
};
203+
204+
inline StackTraceEntry::operator StackTraceError() && {
205+
return StackTraceError().PushEntry(std::move(*this));
206+
}
207+
208+
template <typename T>
209+
inline StackTraceEntry::operator android::base::expected<T,
210+
StackTraceError>() && {
211+
return android::base::unexpected(std::move(*this));
212+
}
213+
214+
} // namespace cuttlefish
215+
216+
/**
217+
* Specialized formatting for a collection of StackTraceEntry elements.
218+
*
219+
* Can be formatted by a `fmt::format` string as {:specifiers}. See
220+
* `fmt::formatter<cuttlefish::StackTraceEntry>` for the format specifiers of
221+
* individual entries. By default the specifier list is passed down to all
222+
* indivudal entries, with the following additional rules. The `^` specifier
223+
* will change the ordering from inner-to-outer instead of outer-to-inner, and
224+
* using the `/` specifier like `<abc>/<xyz>` will apply <xyz> only to the
225+
* innermost stack entry, and <abc> to all other stack entries.
226+
*/
227+
template <>
228+
struct fmt::formatter<cuttlefish::StackTraceError> {
229+
public:
230+
constexpr auto parse(format_parse_context& ctx)
231+
-> format_parse_context::iterator {
232+
auto it = ctx.begin();
233+
while (it != ctx.end() && *it != '}') {
234+
if (*it == 'v') {
235+
for (const auto& spec : StackTraceEntry::kVerbose) {
236+
(has_inner_fmt_spec_ ? inner_fmt_specs_ : fmt_specs_).push_back(spec);
237+
}
238+
} else if (*it == 'V') {
239+
for (const auto& spec : StackTraceEntry::kVeryVerbose) {
240+
(has_inner_fmt_spec_ ? inner_fmt_specs_ : fmt_specs_).push_back(spec);
241+
}
242+
} else if (*it == '/') {
243+
has_inner_fmt_spec_ = true;
244+
} else if (*it == '^') {
245+
inner_to_outer_ = true;
246+
} else {
247+
(has_inner_fmt_spec_ ? inner_fmt_specs_ : fmt_specs_)
248+
.push_back(static_cast<StackTraceEntry::FormatSpecifier>(*it));
249+
}
250+
it++;
251+
}
252+
return it;
253+
}
254+
255+
format_context::iterator format(const cuttlefish::StackTraceError& error,
256+
format_context& ctx) const;
257+
258+
private:
259+
using StackTraceEntry = cuttlefish::StackTraceEntry;
260+
using StackTraceError = cuttlefish::StackTraceError;
261+
262+
bool inner_to_outer_ = false;
263+
bool has_inner_fmt_spec_ = false;
264+
std::vector<StackTraceEntry::FormatSpecifier> fmt_specs_;
265+
std::vector<StackTraceEntry::FormatSpecifier> inner_fmt_specs_;
266+
};

0 commit comments

Comments
 (0)