Skip to content

Commit 829938c

Browse files
committed
Add pretty printing for containers
Example output: ```c++ PrettyContainer(std::vector<int>{1, 2}) ``` formats as ```c++ { 1, 2 } ``` Bug: b/474678754
1 parent 5d16164 commit 829938c

File tree

4 files changed

+271
-0
lines changed

4 files changed

+271
-0
lines changed

base/cvd/cuttlefish/pretty/BUILD.bazel

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,28 @@ package(
44
default_visibility = ["//:android_cuttlefish"],
55
)
66

7+
cf_cc_library(
8+
name = "container",
9+
srcs = ["container.cc"],
10+
hdrs = ["container.h"],
11+
deps = [
12+
"@abseil-cpp//absl/strings",
13+
"@abseil-cpp//absl/strings:str_format",
14+
],
15+
)
16+
17+
cf_cc_test(
18+
name = "container_test",
19+
srcs = ["container_test.cc"],
20+
deps = [
21+
"//cuttlefish/pretty:container",
22+
"@abseil-cpp//absl/strings",
23+
"@fmt",
24+
"@googletest//:gtest",
25+
"@googletest//:gtest_main",
26+
],
27+
)
28+
729
cf_cc_library(
830
name = "struct",
931
srcs = ["struct.cc"],
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// Copyright (C) 2026 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+
#include "cuttlefish/pretty/container.h"
17+
18+
#include <ostream>
19+
#include <string>
20+
#include <string_view>
21+
#include <vector>
22+
23+
#include "absl/strings/str_replace.h"
24+
25+
namespace cuttlefish {
26+
27+
void PrettyContainerType::MemberInternal(std::string_view line) {
28+
members_.emplace_back(absl::StrReplaceAll(line, {{"\n", "\n "}}));
29+
}
30+
31+
std::ostream& operator<<(std::ostream& out, const PrettyContainerType& pc) {
32+
return out << absl::StreamFormat("%v", pc);
33+
}
34+
35+
// For libfmt
36+
std::string format_as(const PrettyContainerType& pc) {
37+
return absl::StrCat(pc);
38+
}
39+
40+
} // namespace cuttlefish
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//
2+
// Copyright (C) 2026 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 <ostream>
19+
#include <string>
20+
#include <string_view>
21+
#include <type_traits>
22+
#include <utility>
23+
#include <vector>
24+
25+
#include "absl/strings/str_cat.h"
26+
#include "absl/strings/str_format.h"
27+
#include "absl/strings/str_join.h"
28+
29+
namespace cuttlefish {
30+
namespace {
31+
32+
template <typename T>
33+
constexpr bool ShouldQuote() {
34+
using Simplified = std::decay_t<T>;
35+
bool is_string = std::is_same_v<Simplified, std::string>;
36+
bool is_string_view = std::is_same_v<Simplified, std::string_view>;
37+
bool is_char_ptr = std::is_same_v<Simplified, char*>;
38+
return is_string || is_string_view || is_char_ptr;
39+
};
40+
41+
} // namespace
42+
43+
/**
44+
* Pretty-prints a container. Invoke this using the `PrettyContainer` overloads.
45+
*
46+
* Example output:
47+
* ```
48+
* PrettyContainer(std::vector<int>{1, 2})
49+
* ```
50+
* formats as
51+
* ```
52+
* {
53+
* 1,
54+
* 2
55+
* }
56+
* ```
57+
*
58+
*/
59+
class PrettyContainerType {
60+
public:
61+
template <typename T, typename FmtMemberFn>
62+
friend PrettyContainerType PrettyContainer(const T& container,
63+
FmtMemberFn format_member_fn);
64+
65+
template <typename T>
66+
friend PrettyContainerType PrettyContainer(const T& container);
67+
68+
template <typename Sink>
69+
friend void AbslStringify(Sink& sink, const PrettyContainerType& pc) {
70+
if (pc.members_.empty()) {
71+
sink.Append("{}");
72+
} else {
73+
absl::Format(&sink, "{\n %v\n}", absl::StrJoin(pc.members_, ",\n "));
74+
}
75+
}
76+
77+
private:
78+
PrettyContainerType() = default;
79+
80+
void MemberInternal(std::string_view);
81+
82+
std::vector<std::string> members_;
83+
};
84+
85+
template <typename T, typename FmtMemberFn>
86+
PrettyContainerType PrettyContainer(const T& container,
87+
FmtMemberFn format_member_fn) {
88+
PrettyContainerType pretty;
89+
for (const auto& member : container) {
90+
std::string formatted =
91+
ShouldQuote<decltype(member)>()
92+
? absl::StrCat("\"", format_member_fn(member), "\"")
93+
: absl::StrCat(format_member_fn(member));
94+
pretty.MemberInternal(formatted);
95+
}
96+
return pretty;
97+
}
98+
99+
template <typename T>
100+
PrettyContainerType PrettyContainer(const T& container) {
101+
return PrettyContainer(container,
102+
[](auto&& v) { return std::forward<decltype(v)>(v); });
103+
}
104+
105+
std::ostream& operator<<(std::ostream& out, const PrettyContainerType&);
106+
107+
// For libfmt
108+
std::string format_as(const PrettyContainerType& pc);
109+
110+
} // namespace cuttlefish
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright (C) 2021 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+
17+
#include "cuttlefish/pretty/container.h"
18+
19+
#include <string_view>
20+
#include <vector>
21+
22+
#include "absl/strings/ascii.h"
23+
#include "fmt/format.h"
24+
#include "gtest/gtest.h"
25+
26+
namespace cuttlefish {
27+
namespace {
28+
29+
void ExpectFormatsTo(const PrettyContainerType& ps,
30+
const std::string_view expected) {
31+
const std::string_view trimmed = absl::StripAsciiWhitespace(expected);
32+
33+
EXPECT_EQ(absl::StrCat(ps), trimmed);
34+
EXPECT_EQ(fmt::format("{}", ps), trimmed);
35+
36+
std::stringstream sstream;
37+
sstream << ps;
38+
EXPECT_EQ(sstream.str(), trimmed);
39+
}
40+
41+
} // namespace
42+
43+
TEST(PrettyStruct, Empty) {
44+
ExpectFormatsTo(PrettyContainer(std::vector<int>{}), "{}");
45+
}
46+
47+
TEST(PrettyStruct, OneMember) {
48+
ExpectFormatsTo(PrettyContainer(std::vector<int>{1}), R"(
49+
{
50+
1
51+
}
52+
)");
53+
}
54+
55+
TEST(PrettyStruct, StringMember) {
56+
ExpectFormatsTo(PrettyContainer(std::vector<std::string_view>{"abc"}), R"(
57+
{
58+
"abc"
59+
}
60+
)");
61+
}
62+
63+
TEST(PrettyStruct, TwoMembers) {
64+
ExpectFormatsTo(PrettyContainer(std::vector<int>{1, 2}), R"(
65+
{
66+
1,
67+
2
68+
}
69+
)");
70+
}
71+
72+
TEST(PrettyStruct, MembersWithNewlines) {
73+
ExpectFormatsTo(PrettyContainer(std::vector<std::string_view>{"abc\ndef"}),
74+
R"(
75+
{
76+
"abc
77+
def"
78+
}
79+
)");
80+
}
81+
82+
TEST(PrettyStruct, NestedMember) {
83+
std::vector<std::vector<int>> container = {{1, 2}, {3, 4}};
84+
ExpectFormatsTo(PrettyContainer(container, PrettyContainer<std::vector<int>>),
85+
R"(
86+
{
87+
{
88+
1,
89+
2
90+
},
91+
{
92+
3,
93+
4
94+
}
95+
}
96+
)");
97+
}
98+
99+
} // namespace cuttlefish

0 commit comments

Comments
 (0)