1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use crate::Row;
use csv::StringRecordIter;
use std::collections::BTreeMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct Headers {
indexes: BTreeMap<String, usize>,
row: Row,
}
pub enum RenameError {
DuplicateColumn(usize),
MissingColumn,
}
impl fmt::Display for RenameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenameError::DuplicateColumn(index) => write!(f, "Duplicate column at index {}", index),
RenameError::MissingColumn => write!(f, "Missing column"),
}
}
}
impl Headers {
pub fn new() -> Self {
Headers {
indexes: BTreeMap::new(),
row: Row::new(),
}
}
pub fn rename(&mut self, from: &str, to: &str) -> Result<(), RenameError> {
if let Some(index) = self.get_index(to) {
return Err(RenameError::DuplicateColumn(index));
}
let index = match self.indexes.remove(from) {
Some(index) => index,
None => return Err(RenameError::MissingColumn),
};
self.indexes.insert(to.to_string(), index);
let mut row_vec: Vec<_> = self.row.into_iter().collect();
row_vec[index] = to;
self.row = row_vec.into_iter().collect();
Ok(())
}
pub fn push_field(&mut self, name: &str) -> bool {
if self.indexes.contains_key(name) {
return false;
}
self.row.push_field(name);
self.indexes.insert(name.to_string(), self.row.len() - 1);
true
}
pub fn contains(&self, name: &str) -> bool {
self.indexes.contains_key(name)
}
pub fn get_field<'a>(&self, row: &'a Row, name: &str) -> Option<&'a str> {
self.indexes.get(name).and_then(|index| row.get(*index))
}
pub fn get_index(&self, name: &str) -> Option<usize> {
self.indexes.get(name).copied()
}
pub fn get_row(&self) -> &Row {
&self.row
}
pub fn from_row(row: Row) -> Result<Self, String> {
let mut header = Headers::new();
for field in &row {
let added = header.push_field(field);
if !added {
return Err(field.to_string());
}
}
Ok(header)
}
}
impl<'a> IntoIterator for &'a Headers {
type Item = &'a str;
type IntoIter = StringRecordIter<'a>;
fn into_iter(self) -> StringRecordIter<'a> {
self.row.into_iter()
}
}
impl From<Headers> for Row {
fn from(headers: Headers) -> Row {
headers.row
}
}