C++Spec 1.0.0
BDD testing for C++
Loading...
Searching...
No Matches
pretty_matchers.hpp
Go to the documentation of this file.
1
2#pragma once
3
4#include <algorithm>
5#include <optional>
6#include <regex>
7#include <string>
8#include <utility>
9#include <vector>
10
11#include "prettyprint.hpp"
12#include "util.hpp"
13
14// C++26 (P3168) gives std::optional begin()/end(), so cxx-prettyprint's
15// has_begin_end/has_const_iterator detection now classifies optional<T> as a
16// container and tries to element-print its value_type — which hard-errors for
17// element types lacking operator<< (and is wrong regardless: an optional is not
18// a container). Exclude it from prettyprint's container detection so optionals
19// fall back to the generic (non-streamable) to_word path.
20namespace pretty_print {
21template <typename T>
22struct is_container<std::optional<T>> : std::false_type {};
23} // namespace pretty_print
24
25namespace CppSpec {
26
27namespace Matchers {
28template <typename A, typename E>
29class MatcherBase;
30}
31
39struct Pretty {
40 std::string _name;
41 [[nodiscard]] std::string name(const std::string& name) const;
42 [[nodiscard]] std::string name_to_sentence(const std::string& name) const;
43 static std::string split_words(const std::string& sym);
44 static std::string underscore(const std::string& camel_cased_word);
45 static std::string last(const std::string& s, char delim);
46 static std::string improve_hash_formatting(const std::string& inspect_string);
47
48 template <Util::is_streamable T>
49 static std::string to_word(const T& item);
50
51 template <typename T>
52 static std::string to_word(const T& item);
53
54 template <typename T>
55 static std::string to_word_type(const T& item);
56
57 template <typename A, typename E>
58 static std::string to_word(const Matchers::MatcherBase<A, E>& matcher);
59
60 template <class T>
61 static std::string to_sentence(const T& item);
62
63 template <class T>
64 static std::string to_sentence(const std::vector<T>& words);
65
66 template <typename T>
67 static std::string inspect_object(const T& object);
68};
69
78template <typename T>
79inline std::string Pretty::to_sentence(const std::vector<T>& objects) {
80 std::vector<std::string> words;
81 for (const auto& object : objects) {
82 words.push_back(to_word(object));
83 }
84
85 std::stringstream ss;
86 switch (words.size()) {
87 case 0:
88 return "";
89 case 1:
90 ss << " " << words[0];
91 break;
92 case 2:
93 ss << " " << words[0] << " and " << words[1];
94 break;
95 default:
96 ss << " ";
97 for (size_t i = 0; i < words.size() - 1; ++i) {
98 if (i != 0) {
99 ss << ", ";
100 }
101 ss << words[i];
102 }
103 ss << ", and " << words.back();
104 break;
105 }
106
107 return ss.str();
108}
109
117template <typename T>
118inline std::string Pretty::to_sentence(const T& item) {
119 return to_sentence(std::vector{item});
120}
121
133template <Util::is_streamable T>
134inline std::string Pretty::to_word(const T& item) {
135 std::ostringstream oss;
136 oss << item; // use operator<< to convert and append
137 return oss.str();
138}
139
140template <>
141inline std::string Pretty::to_word<bool>(const bool& item) {
142 return item ? "true" : "false";
143}
144
145template <>
146inline std::string Pretty::to_word<std::true_type>(const std::true_type& /* item */) {
147 return "true";
148}
149
150template <>
151inline std::string Pretty::to_word<std::false_type>(const std::false_type& /* item */) {
152 return "false";
153}
154
162template <typename T>
163inline std::string Pretty::to_word(const T& item) {
164 std::stringstream ss;
165 // Ruby-style inspect for objects without an overloaded operator<<
166 ss << "#<" << Util::demangle(typeid(item).name()) << ":" << &item << ">";
167 return ss.str();
168}
169
181template <typename A, typename E>
182inline std::string Pretty::to_word(const Matchers::MatcherBase<A, E>& matcher) {
183 std::string description = matcher.description();
184 if (description.empty()) {
185 return "[No description]";
186 }
187 return description;
188}
189
190template <typename T>
191inline std::string Pretty::to_word_type(const T& item) {
192 std::string word = to_word(item);
193 if constexpr (Util::is_streamable<T>) {
194 word += " : " + Util::demangle(typeid(T).name());
195 }
196 return word;
197}
198
199inline std::string Pretty::name_to_sentence(const std::string& n) const {
200 return split_words(name(n));
201}
202
203inline std::string Pretty::name(const std::string& name) const {
204 if (_name.empty()) {
205 return last(name, ':');
206 }
207 return _name;
208}
209
210inline std::string Pretty::split_words(const std::string& sym) {
211 return std::regex_replace(sym, std::regex("_"), " ");
212}
213
214inline std::string Pretty::underscore(const std::string& word) {
215 std::string str = std::regex_replace(word, std::regex("([A-Z]+)([A-Z][a-z])"), "$1_$2");
216 str = std::regex_replace(str, std::regex("([a-z\\d])([A-Z])"), "$1_$2");
217 str = std::regex_replace(str, std::regex("-"), "_");
218 std::ranges::transform(str, str.begin(), ::tolower);
219 return str;
220}
221
222inline std::string Pretty::last(const std::string& s, const char delim) {
223 std::vector<std::string> elems;
224 std::stringstream ss(s);
225 std::string item;
226 while (getline(ss, item, delim)) {
227 if (!item.empty()) {
228 elems.push_back(item);
229 }
230 }
231 return elems.back();
232}
233
234inline std::string Pretty::improve_hash_formatting(const std::string& inspect_string) {
235 return std::regex_replace(inspect_string, std::regex("(\\S)=>(\\S)"), "$1 => $2");
236}
237
245template <typename O>
246inline std::string Pretty::inspect_object(const O& o) {
247 return std::format("({}) => {}", Util::demangle(typeid(o).name()), to_word(o));
248}
249
257template <>
258inline std::string Pretty::inspect_object<const char*>(const char* const& o) {
259 return std::format("(const char *) => \"{}\"", o);
260}
261
269template <>
270inline std::string Pretty::inspect_object<std::string>(const std::string& o) {
271 return std::format("(std::string) => \"{}\"", o);
272}
273
274template <>
275inline std::string Pretty::inspect_object<std::string_view>(const std::string_view& o) {
276 return std::format("(std::string_view) => \"{}\"", o);
277}
278
279} // namespace CppSpec
the base class for all Matcher classes and objects
Definition matcher_base.hpp:37
virtual std::string description()
Get the description of the Matcher.
Definition matcher_base.hpp:138
Checks whether T can be streamed.
Definition util.hpp:81
A helper base class that assists in pretty-printing various objects.
Definition pretty_matchers.hpp:39
static std::string to_word(const T &item)
Formats an object as a string when operator<< is available.
Definition pretty_matchers.hpp:134
static std::string to_sentence(const T &item)
Take a single object and format it as a sentance.
Definition pretty_matchers.hpp:118
Utility functions and classes.
std::string demangle(const char *name)
Definition util.hpp:38