csvzall 0.4.0
CSV command-line tool and emerging C++ library
Loading...
Searching...
No Matches
head.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <csv.hpp>
4
5#include <algorithm>
6#include <cstdint>
7#include <iomanip>
8#include <istream>
9#include <memory>
10#include <ostream>
11#include <sstream>
12#include <string>
13#include <string_view>
14#include <utility>
15#include <vector>
16
17#include "pipeline_types.hpp"
19
20namespace csvzall::head {
21
22struct Result {
23 std::uint64_t rows_processed = 0;
24 std::uint64_t bytes_processed = 0;
25};
26
27inline std::string SanitizeCell(std::string value) {
28 for (char& ch : value) {
29 if (ch == '\n' || ch == '\r' || ch == '\t') {
30 ch = ' ';
31 }
32 }
33 return value;
34}
35
36inline std::string TruncateCell(std::string_view value, std::size_t width) {
37 if (value.size() <= width) {
38 return std::string(value);
39 }
40
41 if (width <= 3) {
42 return std::string(width, '.');
43 }
44
45 return std::string(value.substr(0, width - 3)) + "...";
46}
47
48inline void PrintSeparator(const std::vector<std::size_t>& widths, std::ostream& out) {
49 out << '+';
50 for (const auto width : widths) {
51 out << std::string(width + 2, '-') << '+';
52 }
53 out << '\n';
54}
55
56inline void PrintTableRow(const std::vector<std::string>& values, const std::vector<std::size_t>& widths,
57 std::ostream& out) {
58 out << '|';
59 for (std::size_t i = 0; i < widths.size(); ++i) {
60 std::string value;
61 if (i < values.size()) {
62 value = TruncateCell(values[i], widths[i]);
63 }
64 out << ' ' << std::left << std::setw(static_cast<int>(widths[i])) << value << " |";
65 }
66 out << '\n';
67}
68
69template <typename LoggerT>
70int Run(std::size_t row_limit, std::istream& input, std::ostream& output,
71 const csvzall::pipeline::RunOptions& options,
72 LoggerT& logger, Result& result) {
73 try {
74 std::unique_ptr<std::istringstream> buffered_stdin;
75 std::istream* parse_input = &input;
76 if (options.input_is_stdin) {
77 std::ostringstream raw;
78 raw << input.rdbuf();
79 buffered_stdin = std::make_unique<std::istringstream>(raw.str());
80 parse_input = buffered_stdin.get();
81 logger.Verbose("Buffered stdin for CSV parsing.");
82 }
83
84 csv::CSVFormat format;
85 if (options.delimiter) {
86 format.delimiter(*options.delimiter);
87 } else {
88 format.delimiter({ ',', '|', '\t', ';', '^' });
89 }
90 format.quote('"').header_row(0);
91 std::unique_ptr<csv::CSVReader> owned_reader;
92 if (!options.input_is_stdin && !options.input_path.empty() && options.input_path != "-") {
93 owned_reader = std::make_unique<csv::CSVReader>(
95 options.input_path, options.zip_entry, format));
96 } else {
97 owned_reader = std::make_unique<csv::CSVReader>(*parse_input, format);
98 }
99 csv::CSVReader& reader = *owned_reader;
100
101 const auto headers = reader.get_col_names();
102 if (headers.empty()) {
103 logger.Error("Input appears to have no header row.");
104 return 1;
105 }
106
107 std::vector<std::vector<std::string>> rows;
108 rows.reserve(row_limit);
109
110 for (auto& row : reader) {
111 if (rows.size() >= row_limit) {
112 break;
113 }
114
115 std::vector<std::string> values;
116 values.reserve(headers.size());
117
118 for (std::size_t i = 0; i < headers.size(); ++i) {
119 if (i < row.size()) {
120 auto cell = row[i].get<std::string>();
121 result.bytes_processed += static_cast<std::uint64_t>(cell.size());
122 values.push_back(SanitizeCell(std::move(cell)));
123 } else {
124 values.emplace_back("");
125 }
126 }
127
128 rows.push_back(std::move(values));
129 result.rows_processed++;
130 }
131
132 std::vector<std::size_t> widths(headers.size(), 0);
133 constexpr std::size_t kMaxColumnWidth = 40;
134
135 for (std::size_t i = 0; i < headers.size(); ++i) {
136 widths[i] = std::min(headers[i].size(), kMaxColumnWidth);
137 }
138
139 for (const auto& values : rows) {
140 for (std::size_t i = 0; i < values.size(); ++i) {
141 widths[i] = std::max(widths[i], std::min(values[i].size(), kMaxColumnWidth));
142 }
143 }
144
145 PrintSeparator(widths, output);
146 PrintTableRow(headers, widths, output);
147 PrintSeparator(widths, output);
148 for (const auto& values : rows) {
149 PrintTableRow(values, widths, output);
150 }
151 PrintSeparator(widths, output);
152
153 logger.Verbose("Displayed " + std::to_string(rows.size()) + " row(s) plus header.");
154 return 0;
155 } catch (const std::exception& ex) {
156 logger.Error(std::string("Failed to parse CSV for head: ") + ex.what());
157 return 1;
158 }
159}
160
161} // namespace csvzall::head
std::string raw
Definition heatmap.cpp:29
Definition head.hpp:20
void PrintTableRow(const std::vector< std::string > &values, const std::vector< std::size_t > &widths, std::ostream &out)
Definition head.hpp:56
int Run(std::size_t row_limit, std::istream &input, std::ostream &output, const csvzall::pipeline::RunOptions &options, LoggerT &logger, Result &result)
Definition head.hpp:70
std::string SanitizeCell(std::string value)
Definition head.hpp:27
std::string TruncateCell(std::string_view value, std::size_t width)
Definition head.hpp:36
void PrintSeparator(const std::vector< std::size_t > &widths, std::ostream &out)
Definition head.hpp:48
csv::CSVReader OpenCsvReader(const std::string &path, const std::string &zip_entry, Args &&... args)
Definition gzip_stream.hpp:51
Definition head.hpp:22
std::uint64_t bytes_processed
Definition head.hpp:24
std::uint64_t rows_processed
Definition head.hpp:23
Definition pipeline_types.hpp:16
std::optional< char > delimiter
Definition pipeline_types.hpp:23
std::string input_path
Definition pipeline_types.hpp:34
std::string zip_entry
Definition pipeline_types.hpp:37
bool input_is_stdin
Definition pipeline_types.hpp:18
double value
Definition timeseries.cpp:32