use core::ops::Deref;
use std::io::Read;
use csv::{Error as CsvError, Reader, StringRecord};
use crate::record::Record;
#[cfg(feature = "once_cell")]
pub static RECORDS: once_cell::sync::Lazy<Records> = once_cell::sync::Lazy::new(|| {
let csv = include_str!("../data/IP2LOCATION-ISO3166-2.CSV");
Records::from_csv(csv.as_bytes()).unwrap()
});
#[cfg(feature = "once_cell")]
pub static RECORDS_CODE_MAP: once_cell::sync::Lazy<
std::collections::HashMap<country_code::iso3166_2::SubdivisionCode, Record>,
> = once_cell::sync::Lazy::new(|| {
RECORDS
.iter()
.cloned()
.map(|x| (x.code.to_owned(), x))
.collect()
});
#[derive(Debug, Clone)]
pub struct Records(pub Vec<Record>);
impl Deref for Records {
type Target = Vec<Record>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Records {
pub fn from_csv<R: Read>(rdr: R) -> Result<Self, RecordsFromCsvError> {
let mut rdr = Reader::from_reader(rdr);
let mut inner = vec![];
for record in rdr.records() {
let record = record.map_err(RecordsFromCsvError::CsvParseFailed)?;
let record = if record.get(2) == Some("-") {
StringRecord::from(vec![
&record[0],
&record[1],
format!("{}-", &record[0]).as_str(),
])
} else {
record
};
let row: Record = record
.deserialize(None)
.map_err(RecordsFromCsvError::RecordDeFailed)?;
inner.push(row);
}
Ok(Self(inner))
}
}
#[derive(Debug)]
pub enum RecordsFromCsvError {
CsvParseFailed(CsvError),
RecordDeFailed(CsvError),
}
impl core::fmt::Display for RecordsFromCsvError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for RecordsFromCsvError {}