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())
}
}