1use std::fmt;
2use std::fs::File;
3use std::io::{self, BufRead, BufReader, BufWriter, Write};
4use std::path::Path;
5
6const CSV_SEP: &str = ",";
7
8type CsvCell = String;
9type CsvHead = Vec<CsvCell>;
10type CsvRow = Vec<CsvCell>;
11
12#[derive(Debug, Default)]
18pub struct CsvFile {
19 heads: CsvHead,
20 rows: Vec<CsvRow>,
21}
22
23impl CsvFile {
24 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn heads(&self) -> &CsvHead {
39 &self.heads
40 }
41
42 pub fn head_pos(&self, name: &str) -> Option<usize> {
48 self.heads.iter().position(|head| head.eq(name))
49 }
50
51 pub fn rows(&self) -> &Vec<CsvRow> {
57 &self.rows
58 }
59
60 pub fn cols(&self, name: &str) -> Option<CsvRow> {
66 let index = self.head_pos(name)?;
67
68 let cols = self
69 .rows
70 .iter()
71 .map(|row| {
72 if let Some(data) = row.get(index) {
73 data.to_string()
74 } else {
75 "".to_string()
76 }
77 })
78 .collect::<Vec<String>>();
79
80 Some(cols)
81 }
82
83 pub fn row(&self, position: usize) -> Option<&CsvRow> {
89 self.rows.get(position)
90 }
91
92 pub fn cell(&self, row: usize, col: usize) -> Option<&CsvCell> {
98 let row_data = self.rows.get(row)?;
99
100 row_data.get(col)
101 }
102}
103
104impl CsvFile {
105 pub fn push_head(&mut self, name: &str) {
111 self.heads.push(name.to_string());
112 }
113
114 pub fn set_head(&mut self, position: usize, name: &str) {
120 if let None = self.heads.get(position) {
121 return;
122 }
123
124 self.heads[position] = name.to_string();
125 }
126
127 pub fn insert_head(&mut self, position: usize, name: &str) {
133 self.heads.insert(position, name.to_string());
134 }
135
136 pub fn delete_head(&mut self, position: usize) -> String {
142 self.heads.remove(position)
143 }
144
145 pub fn pop_head(&mut self) -> Option<String> {
151 self.heads.pop()
152 }
153
154 pub fn push_col(&mut self, row: usize, value: &str) {
160 if let None = self.rows.get(row) {
161 return;
162 }
163
164 self.rows[row].push(value.to_string());
165 }
166
167 pub fn set_col(&mut self, row: usize, col: usize, value: &str) {
173 if let None = self.rows.get(row) {
174 return;
175 }
176
177 if let None = self.rows[row].get(col) {
178 return;
179 }
180
181 self.rows[row][col] = value.to_string();
182 }
183
184 pub fn insert_col(&mut self, row: usize, position: usize, value: &str) {
190 self.rows[row].insert(position, value.to_string())
191 }
192
193 pub fn delete_col(&mut self, position: usize) {
199 self.delete_head(position);
200
201 for row in self.rows.iter_mut() {
203 if let None = row.get(position) {
204 continue;
205 }
206
207 row.remove(position);
208 }
209 }
210
211 pub fn pop_col(&mut self) {
217 self.pop_head();
218
219 self.rows.iter_mut().for_each(|row| {
221 row.pop();
222 });
223 }
224
225 pub fn push_row(&mut self, value: &[&str]) {
231 self.rows.push(self.row_mapper(value));
232 }
233
234 pub fn set_row(&mut self, position: usize, value: &[&str]) {
240 if let None = self.rows.get(position) {
241 return;
242 }
243
244 self.rows[position] = self.row_mapper(value);
245 }
246
247 pub fn insert_row(&mut self, position: usize, value: &[&str]) {
253 self.rows.insert(position, self.row_mapper(value));
254 }
255
256 pub fn delete_row(&mut self, position: usize) -> Option<Vec<String>> {
262 if let None = self.rows.get(position) {
263 return None;
264 }
265
266 Some(self.rows.remove(position))
267 }
268
269 pub fn pop_row(&mut self) -> Option<Vec<String>> {
275 self.rows.pop()
276 }
277
278 fn row_mapper(&self, value: &[&str]) -> CsvRow {
284 value
285 .iter()
286 .map(std::string::ToString::to_string)
287 .collect::<Vec<String>>()
288 }
289}
290
291impl CsvFile {
292 pub fn read<P: AsRef<Path>>(path: P) -> Result<CsvFile, io::Error> {
298 let file = File::open(path)?;
299 let buf = BufReader::new(file);
300
301 let contents = buf
302 .lines()
303 .flatten()
304 .filter(|line| !line.is_empty())
305 .map(|row| {
306 row.split(CSV_SEP)
307 .map(std::string::ToString::to_string)
308 .collect::<Vec<String>>()
309 })
310 .collect::<Vec<CsvRow>>();
311
312 Ok(CsvFile {
313 heads: contents[0].clone(),
314 rows: contents[1..].to_vec(),
315 })
316 }
317
318 pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<(), io::Error> {
324 let file = File::create(path)?;
325 let mut buf = BufWriter::new(file);
326
327 if !self.heads.is_empty() {
328 buf.write(self.heads().join(CSV_SEP).as_bytes())?;
329 buf.write("\n".as_bytes())?;
330 }
331
332 for row in self.rows().into_iter() {
333 buf.write(row.join(CSV_SEP).as_bytes())?;
334 buf.write("\n".as_bytes())?;
335 }
336
337 buf.flush()
338 }
339}
340
341impl fmt::Display for CsvFile {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 for head in self.heads.iter() {
345 write!(f, "- {} -", "-".repeat(head.len()))?;
346 }
347 write!(f, "\n")?;
348
349 for head in self.heads.iter() {
351 write!(f, "- {} -", head)?;
352 }
353 write!(f, "\n")?;
354
355 for head in self.heads.iter() {
357 write!(f, "- {} -", "-".repeat(head.len()))?;
358 }
359 write!(f, "\n")?;
360
361 for row in self.rows.iter() {
363 for col in row.iter() {
364 write!(f, "- {} -", col)?;
365 }
366
367 write!(f, "\n")?;
368 }
369
370 write!(f, "")
371 }
372}