use csv::StringRecordIter;
use std::cmp::PartialEq;
use std::collections::HashMap;
use crate::Row;
/// A structure for keeping relationship between the headers and their positions
#[derive(Debug, Clone, PartialEq)]
pub struct Headers {
indexes: HashMap<String, usize>,
names: Row,
}
impl Headers {
/// Creates an empty Headers object.
pub fn new() -> Headers {
Headers {
indexes: HashMap::new(),
names: Row::new(),
}
}
/// Retrieves a field from a row given it's header name
///
/// ```rust
/// use csvsc::{Headers, Row};
///
/// let headers: Headers = Row::from(vec!["id", "val"]).into();
/// let row = Row::from(vec!["1", "40"]);
///
/// assert_eq!(headers.get_field(&row, "id"), Some("1"));
/// assert_eq!(headers.get_field(&row, "val"), Some("40"));
/// assert_eq!(headers.get_field(&row, "foo"), None);
/// ```
pub fn get_field<'r>(&self, row: &'r Row, field: &str) -> Option<&'r str> {
self.index(field).and_then(|i| row.get(i))
}
/// Adds a new header.
///
/// Returns wether the header was added or not.
///
/// ```rust
/// use csvsc::{Headers, Row};
///
/// let mut h: Headers = Row::from(vec!["name"]).into();
///
/// h.add("value");
///
/// assert_eq!(h, Row::from(vec!["name", "value"]).into());
///
/// assert!(!h.add("name"));
/// assert_eq!(h, Row::from(vec!["name", "value"]).into());
/// ```
pub fn add(&mut self, colname: &str) -> bool {
if self.indexes.contains_key(colname) {
return false;
}
self.names.push_field(colname);
self.indexes
.insert(colname.to_string(), self.names.len() - 1);
true
}
pub fn len(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn as_row(&self) -> &Row {
&self.names
}
/// Consumes this Headers object and returns the underlaying Row containing
/// the names.
pub fn into_row(self) -> Row {
self.names
}
/// Obtains the index of the given field in the headers
pub fn index(&self, field: &str) -> Option<usize> {
self.indexes.get(field).copied()
}
pub fn contains_key(&self, field: &str) -> bool {
self.indexes.contains_key(field)
}
pub fn iter(&self) -> StringRecordIter {
self.names.iter()
}
}
impl PartialEq<Headers> for Row {
fn eq(&self, other: &Headers) -> bool {
self == other.as_row()
}
}
impl From<Row> for Headers {
fn from(row: Row) -> Headers {
let mut indexes = HashMap::new();
for (index, entry) in row.iter().enumerate() {
indexes.insert(entry.to_string(), index);
}
Headers {
indexes,
names: row,
}
}
}
impl Default for Headers {
fn default() -> Self {
Self::new()
}
}