csv_stream/
iter.rs

1use serde::Serialize;
2
3use crate::{Result, Writer};
4
5/// An iterable CSV creator
6///
7/// # Example
8///
9/// ```
10/// use std::error::Error;
11/// use csv_stream::WriterBuilder;
12/// use serde::Serialize;
13///
14/// # fn main() { example().unwrap(); }
15/// fn example() -> Result<(), Box<dyn Error>> {
16///     #[derive(Serialize)]
17///     struct Row { foo: usize, bar: usize }
18///     let rows = [
19///         Row{ foo: 1, bar: 2 },
20///         Row{ foo: 3, bar: 4 },
21///     ];
22///
23///     let mut csv_iter = WriterBuilder::default().build_iter(rows);
24///
25///     let mut buf = vec![];
26///     for row in csv_iter {
27///         let row = row.unwrap();
28///         buf.extend_from_slice(&row);
29///     }
30///
31///     let data = String::from_utf8(buf)?;
32///     assert_eq!(data, "foo,bar\n1,2\n3,4\n");
33///     Ok(())
34/// }
35/// ```
36pub struct Iter<I> {
37    iter: I,
38
39    writer: Writer,
40}
41
42impl<I: Iterator> Iter<I> {
43    pub fn new(iter: impl IntoIterator<IntoIter = I>, writer: Writer) -> Self {
44        Self {
45            iter: iter.into_iter(),
46            writer,
47        }
48    }
49}
50
51impl<I: Iterator> Iterator for Iter<I>
52where
53    I::Item: Serialize,
54{
55    type Item = Result<Vec<u8>>;
56
57    fn next(&mut self) -> Option<Self::Item> {
58        let s = self.iter.next()?;
59        let mut buf = vec![];
60        Some(self.writer.serialize(&mut buf, s).map(|_| buf))
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use crate::{Terminator, WriterBuilder};
67    use serde::Serialize;
68
69    use super::Iter;
70
71    #[derive(Serialize)]
72    struct Row<'a> {
73        city: &'a str,
74        country: &'a str,
75        // Serde allows us to name our headers exactly,
76        // even if they don't match our struct field names.
77        #[serde(rename = "popcount")]
78        population: u64,
79    }
80
81    const ROWS: [Row<'static>; 2] = [
82        Row {
83            city: "Boston",
84            country: "United States",
85            population: 4628910,
86        },
87        Row {
88            city: "Concord",
89            country: "United States",
90            population: 42695,
91        },
92    ];
93
94    #[test]
95    fn serialize() {
96
97        let writer = WriterBuilder::default().build();
98
99        let i = Iter::new(ROWS, writer);
100
101        let buf = i
102            .map(Result::unwrap)
103            .flatten()
104            .collect();
105
106        let buf = String::from_utf8(buf).unwrap();
107
108        assert_eq!(
109            buf,
110            r#"city,country,popcount
111Boston,United States,4628910
112Concord,United States,42695
113"#
114        )
115    }
116
117    #[test]
118    fn config() {
119        let writer = WriterBuilder::default()
120            .has_headers(false)
121            .delimiter(b';')
122            .terminator(Terminator::CRLF)
123            .build();
124
125        let i = Iter::new(ROWS, writer);
126
127        let buf = i
128            .map(Result::unwrap)
129            .flatten()
130            .collect();
131
132        let buf = String::from_utf8(buf).unwrap();
133
134        assert_eq!(
135            buf,
136            r#"Boston;United States;4628910
137Concord;United States;42695
138"#.replace("\n", "\r\n")
139        )
140    }
141}