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
use crate::{Error, Row};
use csv::StringRecordIter;
use std::collections::BTreeMap;

/// The headers of a CSV file
#[derive(Debug, Clone, PartialEq)]
pub struct Headers {
	indexes: BTreeMap<String, usize>,
	row: Row,
}
impl Headers {
	pub fn new() -> Self {
		Headers {
			indexes: BTreeMap::new(),
			row: Row::new(),
		}
	}

	/// Returns `false` if `from` is non-existant or if the new name already exists
	pub fn rename(&mut self, from: &str, to: &str) -> Result<(), Error> {
		if self.contains(to) {
			return Err(Error::DuplicateColumn(to.to_string()));
		}
		let index = match self.indexes.remove(from) {
			Some(index) => index,
			None => return Err(Error::MissingColumn(from.to_string())),
		};
		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(())
	}

	/// Returns false if the field already exists
	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, Error> {
		let mut header = Headers::new();
		for field in &row {
			let added = header.push_field(field);
			if !added {
				return Err(Error::DuplicateColumn(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
	}
}