Reviewers: teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1506
116 lines
3.8 KiB
C++
116 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <map>
|
|
|
|
#include "glog/logging.h"
|
|
|
|
#include "communication/bolt/v1/value.hpp"
|
|
#include "utils/algorithm.hpp"
|
|
|
|
// TODO: Why is this here?! It's only used in tests and query/repl.cpp
|
|
|
|
/**
|
|
* A mocker for the data output record stream.
|
|
* This implementation checks that messages are
|
|
* sent to it in an acceptable order, and tracks
|
|
* the content of those messages.
|
|
*/
|
|
template <class TResultValue = communication::bolt::Value>
|
|
class ResultStreamFaker {
|
|
public:
|
|
ResultStreamFaker() = default;
|
|
ResultStreamFaker(const ResultStreamFaker &) = delete;
|
|
ResultStreamFaker &operator=(const ResultStreamFaker &) = delete;
|
|
ResultStreamFaker(ResultStreamFaker &&) = default;
|
|
ResultStreamFaker &operator=(ResultStreamFaker &&) = default;
|
|
|
|
void Header(const std::vector<std::string> &fields) { header_ = fields; }
|
|
|
|
void Result(const std::vector<TResultValue> &values) {
|
|
results_.push_back(values);
|
|
}
|
|
|
|
void Summary(const std::map<std::string, TResultValue> &summary) {
|
|
summary_ = summary;
|
|
}
|
|
|
|
const auto &GetHeader() const { return header_; }
|
|
|
|
const auto &GetResults() const { return results_; }
|
|
|
|
const auto &GetSummary() const { return summary_; }
|
|
|
|
friend std::ostream &operator<<(std::ostream &os,
|
|
const ResultStreamFaker &results) {
|
|
auto decoded_value_to_string = [](const auto &value) {
|
|
std::stringstream ss;
|
|
ss << value;
|
|
return ss.str();
|
|
};
|
|
const std::vector<std::string> &header = results.GetHeader();
|
|
std::vector<int> column_widths(header.size());
|
|
std::transform(header.begin(), header.end(), column_widths.begin(),
|
|
[](const auto &s) { return s.size(); });
|
|
|
|
// convert all the results into strings, and track max column width
|
|
auto &results_data = results.GetResults();
|
|
std::vector<std::vector<std::string>> result_strings(
|
|
results_data.size(), std::vector<std::string>(column_widths.size()));
|
|
for (int row_ind = 0; row_ind < static_cast<int>(results_data.size());
|
|
++row_ind) {
|
|
for (int col_ind = 0; col_ind < static_cast<int>(column_widths.size());
|
|
++col_ind) {
|
|
std::string string_val =
|
|
decoded_value_to_string(results_data[row_ind][col_ind]);
|
|
column_widths[col_ind] =
|
|
std::max(column_widths[col_ind], (int)string_val.size());
|
|
result_strings[row_ind][col_ind] = string_val;
|
|
}
|
|
}
|
|
|
|
// output a results table
|
|
// first define some helper functions
|
|
auto emit_horizontal_line = [&]() {
|
|
os << "+";
|
|
for (auto col_width : column_widths)
|
|
os << std::string((unsigned long)col_width + 2, '-') << "+";
|
|
os << std::endl;
|
|
};
|
|
|
|
auto emit_result_vec = [&](const std::vector<std::string> result_vec) {
|
|
os << "| ";
|
|
for (int col_ind = 0; col_ind < static_cast<int>(column_widths.size());
|
|
++col_ind) {
|
|
const std::string &res = result_vec[col_ind];
|
|
os << res << std::string(column_widths[col_ind] - res.size(), ' ');
|
|
os << " | ";
|
|
}
|
|
os << std::endl;
|
|
};
|
|
|
|
// final output of results
|
|
emit_horizontal_line();
|
|
emit_result_vec(results.GetHeader());
|
|
emit_horizontal_line();
|
|
for (const auto &result_vec : result_strings) emit_result_vec(result_vec);
|
|
emit_horizontal_line();
|
|
os << "Found " << results_data.size() << " matching results" << std::endl;
|
|
|
|
// output the summary
|
|
os << "Query summary: {";
|
|
utils::PrintIterable(os, results.GetSummary(), ", ",
|
|
[&](auto &stream, const auto &kv) {
|
|
stream << kv.first << ": " << kv.second;
|
|
});
|
|
os << "}" << std::endl;
|
|
|
|
return os;
|
|
}
|
|
|
|
private:
|
|
// the data that the record stream can accept
|
|
std::vector<std::string> header_;
|
|
std::vector<std::vector<TResultValue>> results_;
|
|
std::map<std::string, TResultValue> summary_;
|
|
};
|