csv_diff/
csv.rs

1use std::io::{Cursor, Read, Seek};
2
3pub struct Csv<R> {
4    csv_reader: csv::Reader<R>,
5}
6
7impl<R: Read + Seek + Send> Csv<R> {
8    /// Create a new `Csv` with something that can read Csv data and implements [`CsvReadSeek`].
9    /// # Example: use `Csv` together with `CsvByteDiffLocal` to compare CSV data
10    #[cfg_attr(
11        feature = "rayon-threads",
12        doc = r##"
13```
14use csv_diff::{csv_diff::CsvByteDiffLocal, csv::Csv};
15# fn main() -> Result<(), Box<dyn std::error::Error>> {
16let csv_data_left = "id,name,kind\n\
17                    1,lemon,fruit\n\
18                    2,strawberry,fruit";
19let csv_data_right = "id,name,kind\n\
20                    1,lemon,fruit\n\
21                    2,strawberry,fruit";
22
23let csv_byte_diff = CsvByteDiffLocal::new()?;
24
25let mut diff_byte_records = csv_byte_diff.diff(
26    // bytes are not `Seek`able by default, but trait `CsvReadSeek` makes them seekable
27    Csv::with_reader_seek(csv_data_left.as_bytes()),
28    Csv::with_reader_seek(csv_data_right.as_bytes()),
29)?;
30
31let num_of_rows_different = diff_byte_records.as_slice().len();
32
33assert_eq!(
34    num_of_rows_different,
35    0
36);
37Ok(())
38# }
39```
40    "##
41    )]
42    pub fn with_reader_seek<RSeek: CsvReadSeek<R>>(reader: RSeek) -> Self {
43        Self {
44            csv_reader: csv::Reader::from_reader(reader.into_read_seek()),
45        }
46    }
47}
48
49impl<R: Read> Csv<R> {
50    pub fn with_reader(reader: R) -> Self {
51        Self {
52            csv_reader: csv::Reader::from_reader(reader),
53        }
54    }
55}
56
57impl<R> Csv<R> {
58    pub fn into_csv_reader(self) -> csv::Reader<R> {
59        self.csv_reader
60    }
61
62    pub(crate) fn csv_reader_mut(&mut self) -> &mut csv::Reader<R> {
63        &mut self.csv_reader
64    }
65}
66
67impl<R> From<csv::Reader<R>> for Csv<R> {
68    fn from(rdr: csv::Reader<R>) -> Self {
69        Self { csv_reader: rdr }
70    }
71}
72
73/// Produces a CSV reader that implements [`Read`](std::io::Read) + [`Seek`](std::io::Seek) + [`Send`](core::marker::Send).
74pub trait CsvReadSeek<R>
75where
76    R: Read + Seek + Send,
77{
78    /// Converts this value into `R`.
79    fn into_read_seek(self) -> R;
80}
81
82impl<T> CsvReadSeek<Cursor<T>> for T
83where
84    T: AsRef<[u8]> + Send,
85{
86    fn into_read_seek(self) -> Cursor<T> {
87        Cursor::new(self)
88    }
89}
90
91impl<R> CsvReadSeek<R> for R
92where
93    R: Read + Seek + Send,
94{
95    fn into_read_seek(self) -> R {
96        self
97    }
98}
99
100pub trait CsvReaderBuilderExt<R: Read + Seek + Send> {
101    fn from_reader_seek<RSeek: CsvReadSeek<R>>(&self, reader: RSeek) -> csv::Reader<R>;
102}
103
104impl<R: Read + Seek + Send> CsvReaderBuilderExt<R> for csv::ReaderBuilder {
105    fn from_reader_seek<RSeek: CsvReadSeek<R>>(&self, reader: RSeek) -> csv::Reader<R> {
106        self.from_reader(reader.into_read_seek())
107    }
108}