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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use csv::ByteRecord;
use std::cmp::max;
/// Holds both left and right CSV headers (if present).
/// The difference to [`Headers`] is that this holds headers that might __not__ have been parsed successfully.
#[derive(Debug, Default)]
pub struct HeadersParsed {
pub(crate) headers_left: Option<csv::Result<ByteRecord>>,
pub(crate) headers_right: Option<csv::Result<ByteRecord>>,
}
impl HeadersParsed {
pub(crate) fn new(
headers_left: Option<csv::Result<ByteRecord>>,
headers_right: Option<csv::Result<ByteRecord>>,
) -> Self {
Self {
headers_left,
headers_right,
}
}
/// Return the [`Result`] of parsing headers of the left CSV.
/// If the CSV has been parsed with [`has_headers(false)`](https://docs.rs/csv/1.3.0/csv/struct.ReaderBuilder.html#method.has_headers),
/// this will return `None`.
pub fn headers_left(&self) -> Option<Result<&ByteRecord, &csv::Error>> {
Some(self.headers_left.as_ref()?.as_ref())
}
/// Return the [`Result`] of parsing headers of the right CSV.
/// If the CSV has been parsed with [`has_headers(false)`](https://docs.rs/csv/1.3.0/csv/struct.ReaderBuilder.html#method.has_headers),
/// this will return `None`.
pub fn headers_right(&self) -> Option<Result<&ByteRecord, &csv::Error>> {
Some(self.headers_right.as_ref()?.as_ref())
}
pub(crate) fn max_num_cols(&self) -> Option<usize> {
max(
self.headers_left()
.and_then(|hl| hl.as_ref().map(|csv| csv.len()).ok()),
self.headers_right()
.and_then(|hr| hr.as_ref().map(|csv| csv.len()).ok()),
)
}
}
impl
From<(
Option<csv::Result<ByteRecord>>,
Option<csv::Result<ByteRecord>>,
)> for HeadersParsed
{
fn from(
(headers_left, headers_right): (
Option<csv::Result<ByteRecord>>,
Option<csv::Result<ByteRecord>>,
),
) -> Self {
Self::new(headers_left, headers_right)
}
}
/// Holds both left and right CSV headers (if present).
/// The difference to [`HeadersParsed`] is that this holds headers (if present) that have been parsed __successfully__.
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Headers {
pub(crate) headers_left: Option<ByteRecord>,
pub(crate) headers_right: Option<ByteRecord>,
}
impl Headers {
pub(crate) fn new(headers_left: Option<ByteRecord>, headers_right: Option<ByteRecord>) -> Self {
Self {
headers_left,
headers_right,
}
}
/// Return the successfully parsed headers of the left CSV (if present).
/// If the CSV has been parsed with [`has_headers(false)`](https://docs.rs/csv/1.3.0/csv/struct.ReaderBuilder.html#method.has_headers),
/// this will return `None`.
pub fn headers_left(&self) -> Option<&ByteRecord> {
self.headers_left.as_ref()
}
/// Return the successfully parsed headers of the right CSV (if present).
/// If the CSV has been parsed with [`has_headers(false)`](https://docs.rs/csv/1.3.0/csv/struct.ReaderBuilder.html#method.has_headers),
/// this will return `None`.
pub fn headers_right(&self) -> Option<&ByteRecord> {
self.headers_right.as_ref()
}
pub(crate) fn max_num_cols(&self) -> Option<usize> {
max(
self.headers_left().map(|hl| hl.len()),
self.headers_right().map(|hr| hr.len()),
)
}
}
impl From<(Option<ByteRecord>, Option<ByteRecord>)> for Headers {
fn from((headers_left, headers_right): (Option<ByteRecord>, Option<ByteRecord>)) -> Self {
Self::new(headers_left, headers_right)
}
}
impl TryFrom<HeadersParsed> for Headers {
type Error = csv::Error;
fn try_from(value: HeadersParsed) -> Result<Self, Self::Error> {
Ok((
value.headers_left.transpose()?,
value.headers_right.transpose()?,
)
.into())
}
}