Vince's CSV Parser
Loading...
Searching...
No Matches
data_frame_column.hpp
1#pragma once
2
3#include <memory>
4#include <stdexcept>
5#include <string>
6#include <vector>
7
8#include "data_frame_cell.hpp"
9#include "fwd.hpp"
10#include "indexed_proxy_iterator.hpp"
11
12namespace csv {
13 template<typename KeyType>
15 public:
17 DataFrameCell operator()(const DataFrameColumn<KeyType>* owner, size_t row_index) const {
18 return owner->make_cell(row_index, false);
19 }
20 };
21
24
25 DataFrameColumn() : frame_(nullptr), mutable_frame_(nullptr), col_index_(0), generation_value_(0) {}
26
27 DataFrameColumn(const DataFrame<KeyType>* frame, size_t col_index)
28 : frame_(frame),
29 mutable_frame_(nullptr),
30 col_index_(col_index),
31 generation_(frame ? frame->column_generation_ : std::shared_ptr<size_t>()),
32 generation_value_(generation_ ? *generation_ : 0) {}
33
34 DataFrameColumn(DataFrame<KeyType>* frame, size_t col_index)
35 : frame_(frame),
36 mutable_frame_(frame),
37 col_index_(col_index),
38 generation_(frame ? frame->column_generation_ : std::shared_ptr<size_t>()),
39 generation_value_(generation_ ? *generation_ : 0) {}
40
42#if CSV_EXCEPTIONS_ENABLED
43 const std::string& name() const {
44 CSVError error;
45 this->require_valid(error);
46 internals::throw_dataframe_error(error);
47 return frame_->columns()[col_index_];
48 }
49#endif
50
52 DataFrameCell try_field(size_t row_index, CSVError& error) {
53 if (!this->require_valid(error)) return {};
54 return frame_->try_make_cell(row_index, col_index_, mutable_frame_ != nullptr, error);
55 }
56 DataFrameCell try_field(size_t row_index, CSVError& error) const {
57 if (!this->require_valid(error)) return {};
58 return frame_->try_make_cell(row_index, col_index_, false, error);
59 }
60 csv::string_view try_get_sv(size_t row_index, CSVError& error) const {
61 // Retain the explicit owner check for GCC's object-size diagnostics
62 // when an unbound proxy and the subsequent storage access are inlined.
63 if (!this->require_valid(error) || !frame_) return {};
64 const auto binding = frame_->storage_.try_bind(row_index, col_index_, error);
65 (void)binding;
66 return error ? csv::string_view() : frame_->cell_view(row_index, col_index_);
67 }
68 csv::string_view try_name(CSVError& error) const {
69 if (!this->require_valid(error) || !frame_) return {};
70 return csv::string_view(frame_->columns()[col_index_]);
71 }
72 template<typename T = std::string>
73 std::vector<T> try_to_vector(CSVError& error) const {
74 std::vector<T> values;
75 if (!this->require_valid(error)) return values;
76 values.reserve(this->size());
77 for (size_t i = 0; i < this->size(); ++i) {
78 auto cell = this->try_field(i, error);
79 if (error) return values;
80 T value{};
81 CSVConversionError conversion;
82 if (!cell.try_get(value, conversion)) {
83 internals::fail_static(error, CSVErrorCode::ConversionFailed, csv_conversion_error_message(conversion));
84 return values;
85 }
86 values.push_back(std::move(value));
87 }
88 return values;
89 }
90 bool try_erase(CSVError& error) {
91 if (!this->require_valid(error)) return false;
92 if (!mutable_frame_)
93 return internals::fail_static(error, CSVErrorCode::ReadOnlyProxy, "cannot erase column from const DataFrame");
94 return mutable_frame_->erase_column_at_index(col_index_, error);
95 }
96
98 bool is_valid() const noexcept {
99 return frame_ && generation_ && *generation_ == generation_value_
100 && col_index_ < frame_->n_cols();
101 }
102
104 size_t index() const noexcept {
105 return col_index_;
106 }
107
109 size_t size() const noexcept {
110 if (!this->is_valid()) {
111 return 0;
112 }
113
114 return frame_->size();
115 }
116
118 bool empty() const noexcept {
119 return this->size() == 0;
120 }
121
123#if CSV_EXCEPTIONS_ENABLED
124 DataFrameCell operator[](size_t row_index) {
125 CSVError error;
126 auto result = this->try_field(row_index, error);
127 internals::throw_dataframe_error(error);
128 return result;
129 }
130#endif
131
133#if CSV_EXCEPTIONS_ENABLED
134 DataFrameCell operator[](size_t row_index) const {
135 CSVError error;
136 auto result = this->try_field(row_index, error);
137 internals::throw_dataframe_error(error);
138 return result;
139 }
140#endif
141
148#if CSV_EXCEPTIONS_ENABLED
149 csv::string_view get_sv(size_t row_index) const {
150 CSVError error;
151 auto result = this->try_get_sv(row_index, error);
152 internals::throw_dataframe_error(error);
153 return result;
154 }
155#endif
156
158#if CSV_EXCEPTIONS_ENABLED
159 template<typename T = std::string>
160 std::vector<T> to_vector() const {
161 CSVError error;
162 auto result = this->template try_to_vector<T>(error);
163 internals::throw_dataframe_error(error);
164 return result;
165 }
166#endif
167
169#if CSV_EXCEPTIONS_ENABLED
170 operator std::vector<std::string>() const {
171 std::vector<std::string> result;
172 result.reserve(this->size());
173 for (size_t i = 0; i < this->size(); ++i)
174 result.emplace_back(this->make_cell(i, false).get_sv());
175 return result;
176 }
177#endif
178
183#if CSV_EXCEPTIONS_ENABLED
184 bool erase() {
185 CSVError error;
186 const bool result = this->try_erase(error);
187 internals::throw_dataframe_error(error);
188 return result;
189 }
190#endif
191
192#if defined(CSV_HAS_CXX20) && CSV_EXCEPTIONS_ENABLED
197 auto to_sv_range() const {
198 return std::views::iota(size_t{0}, this->size())
199 | std::views::transform([this](size_t row_index) {
200 return std::string(this->make_cell(row_index, false).get_sv());
201 });
202 }
203#endif
204
205#if CSV_EXCEPTIONS_ENABLED
207 iterator begin() const { return iterator(this, 0); }
208 iterator end() const { return iterator(this, this->size()); }
209 const_iterator cbegin() const { return const_iterator(this, 0); }
210 const_iterator cend() const { return const_iterator(this, this->size()); }
211
212#endif
213
214 private:
215 DataFrameCell make_cell(size_t row_index, bool mutable_access) const {
216#if CSV_EXCEPTIONS_ENABLED
217 CSVError error;
218 if (!this->require_valid(error) || !frame_->validate_row_index(row_index, error))
219 internals::throw_dataframe_error(error);
220#else
221 CSV_DEBUG_ASSERT(this->is_valid() && row_index < this->size());
222#endif
223 return frame_->make_cell(row_index, col_index_, mutable_access);
224 }
225
226 bool require_valid(CSVError& error) const {
227 error.clear();
228 if (!frame_ || !generation_ || *generation_ != generation_value_)
229 return internals::fail_static(error, CSVErrorCode::InvalidProxy,
230 "DataFrameColumn proxy is invalid after structural column mutation");
231 return col_index_ < frame_->n_cols() || internals::fail_static(
232 error, CSVErrorCode::IndexOutOfBounds, internals::ERROR_COLUMN_INDEX_OUT_OF_RANGE);
233 }
234
235 const DataFrame<KeyType>* frame_;
236 DataFrame<KeyType>* mutable_frame_;
237 size_t col_index_;
238 std::shared_ptr<size_t> generation_;
239 size_t generation_value_;
240 };
241}
csv::string_view get_sv(size_t row_index) const
Access a visible cell value as a string_view without materializing a DataFrameCell.
DataFrameCell operator[](size_t row_index) const
Access a visible cell value by row index without allowing edits.
bool erase()
Delete this column from the parent DataFrame.
std::vector< T > to_vector() const
Materialize this column as a vector of converted values.
size_t index() const noexcept
Zero-based column position.
size_t size() const noexcept
Number of rows in the parent batch.
const std::string & name() const
Column name.
bool is_valid() const noexcept
Whether this column is bound to an existing position in the active schema.
DataFrameCell try_field(size_t row_index, CSVError &error)
Checked cell access, preserving mutability of the column proxy.
auto to_sv_range() const
Convert this DataFrameColumn into a std::ranges::input_range of strings.
bool empty() const noexcept
Whether the parent batch contains no rows.
iterator begin() const
Iterate over visible cells in this column.
DataFrameCell operator[](size_t row_index)
Access a visible cell value by row index, allowing edits on mutable frames.
size_t n_cols() const noexcept
Get the number of visible columns in the DataFrame.
const std::vector< std::string > & columns() const noexcept
Get the column names in order.
size_t size() const noexcept
Get the number of rows in the DataFrame.
Borrow a row and error state for synchronous, single-pass field access.
The all encompassing namespace.
CSVConversionError
Non-throwing CSVField conversion result.
Definition csv_error.hpp:23
const char * csv_conversion_error_message(CSVConversionError error) noexcept
Return a stable human-readable description for a CSVConversionError.
Definition csv_error.hpp:53
@ IndexOutOfBounds
A row/column position exceeds the visible schema or physical record width.
@ InvalidProxy
A proxy is unbound or a column proxy was invalidated by structural mutation.
@ ReadOnlyProxy
Mutation was requested through a cell, row, or column without edit permission.
@ ConversionFailed
A DataFrame field/key conversion failed; context describes the cause.
std::string_view string_view
The string_view class used by this library.
Definition common.hpp:182
A diagnostic whose text remains valid for the lifetime of the error.
Definition csv_error.hpp:94