csvsc 2.2.1

Build processing chains for CSV files
Documentation
use crate::{ Headers, RowStream, Row, error };


#[derive(Debug)]
pub enum BuildError {
    DuplicatedHeader(String),
}

/// Adds a column to each register using a closure to generate its data.
///
/// The closure is passed the existing headers and current row.
pub struct AddWith<I, F> {
    iter: I,
    f: F,
    headers: Headers,
}

impl<I, F> AddWith<I, F>
where
    I: RowStream,
    F: FnMut(&Headers, &Row) -> error::Result<String>,
{
    pub fn new(iter: I, colname: &str, f: F) -> Result<AddWith<I, F>, BuildError> {
        let mut headers = iter.headers().clone();

        if !headers.add(colname) {
            return Err(BuildError::DuplicatedHeader(colname.to_string()));
        }

        Ok(AddWith{
            iter,
            f,
            headers,
        })
    }
}

pub struct IntoIter<I, F> {
    iter: I,
    f: F,
    headers: Headers,
}

impl<I, F> Iterator for IntoIter<I, F>
where
    I: Iterator<Item = error::RowResult>,
    F: FnMut(&Headers, &Row) -> error::Result<String>,
{
    type Item = error::RowResult;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|result| {
            result.and_then(|mut val| {
                match (self.f)(&self.headers, &val) {
                    Ok(s) => val.push_field(&s),
                    Err(e) => return Err(e),
                }

                Ok(val)
            })
        })
    }
}

impl<I, F> IntoIterator for AddWith<I, F>
where
    I: RowStream,
    F: FnMut(&Headers, &Row) -> error::Result<String>,
{
    type Item = error::RowResult;

    type IntoIter = IntoIter<I::IntoIter, F>;

    fn into_iter(self) -> Self::IntoIter {
        Self::IntoIter {
            iter: self.iter.into_iter(),
            f: self.f,
            headers: self.headers,
        }
    }
}

impl<I, F> RowStream for AddWith<I, F>
where
    I: RowStream,
    F: FnMut(&Headers, &Row) -> error::Result<String>,
{
    fn headers(&self) -> &Headers {
        &self.headers
    }
}

#[cfg(test)]
mod tests {
    use super::{AddWith, Row, RowStream};
    use crate::mock::MockStream;

    #[test]
    fn test_add() {
        let iter = MockStream::from_rows(
            vec![
                Ok(Row::from(vec!["id"])),
                Ok(Row::from(vec!["1"])),
                Ok(Row::from(vec!["2"])),
                Ok(Row::from(vec!["3"])),
                Ok(Row::from(vec!["4"])),
            ]
            .into_iter(),
        )
        .unwrap();

        let add= AddWith::new(
            iter,
            "col",
            |headers, row| {
                let v: i32 = headers.get_field(row, "id").unwrap().parse().unwrap();

                Ok((v*v).to_string())
            }
        ).unwrap();

        assert_eq!(
            *add.headers(),
            Row::from(vec!["id", "col"]).into(),
        );

        let mut add= add.into_iter();

        assert_eq!(
            add.next().unwrap().unwrap(),
            Row::from(vec!["1", "1"])
        );
        assert_eq!(
            add.next().unwrap().unwrap(),
            Row::from(vec!["2", "4"])
        );
        assert_eq!(
            add.next().unwrap().unwrap(),
            Row::from(vec!["3", "9"])
        );
        assert_eq!(
            add.next().unwrap().unwrap(),
            Row::from(vec!["4", "16"])
        );
    }
}