use std::path::Path;
use crate::RrfError;
pub trait FromRecord: Sized {
fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError>;
}
struct Fields<'a> {
record: &'a csv::ByteRecord,
path: &'a Path,
line: u64,
}
impl Fields<'_> {
fn new<'a>(
record: &'a csv::ByteRecord,
path: &'a Path,
line: u64,
expected: usize,
) -> Result<Fields<'a>, RrfError> {
if record.len() < expected {
return Err(RrfError::Columns {
path: path.to_path_buf(),
line,
columns: record.len(),
expected,
});
}
Ok(Fields { record, path, line })
}
fn text(&self, index: usize) -> Result<String, RrfError> {
let bytes = self.record.get(index).unwrap_or_default();
std::str::from_utf8(bytes)
.map(str::to_owned)
.map_err(|source| RrfError::Utf8 {
path: self.path.to_path_buf(),
line: self.line,
source,
})
}
fn optional(&self, index: usize) -> Result<Option<String>, RrfError> {
let text = self.text(index)?;
Ok((!text.is_empty()).then_some(text))
}
fn id(&self, index: usize) -> Result<u64, RrfError> {
let text = self.text(index)?;
text.parse().map_err(|source| RrfError::Identifier {
path: self.path.to_path_buf(),
line: self.line,
value: text,
source,
})
}
fn optional_id(&self, index: usize) -> Result<Option<u64>, RrfError> {
match self.optional(index)? {
None => Ok(None),
Some(text) => text
.parse()
.map(Some)
.map_err(|source| RrfError::Identifier {
path: self.path.to_path_buf(),
line: self.line,
value: text,
source,
}),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Atom {
pub rxcui: u64,
pub language: String,
pub rxaui: u64,
pub sab: String,
pub tty: String,
pub code: String,
pub name: String,
pub suppress: String,
}
impl FromRecord for Atom {
fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
let f = Fields::new(record, path, line, 18)?;
Ok(Self {
rxcui: f.id(0)?,
language: f.text(1)?,
rxaui: f.id(7)?,
sab: f.text(11)?,
tty: f.text(12)?,
code: f.text(13)?,
name: f.text(14)?,
suppress: f.text(16)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Relationship {
pub rxcui1: Option<u64>,
pub rxaui1: Option<u64>,
pub rel: String,
pub rxcui2: Option<u64>,
pub rxaui2: Option<u64>,
pub rela: Option<String>,
pub sab: String,
}
impl FromRecord for Relationship {
fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
let f = Fields::new(record, path, line, 16)?;
Ok(Self {
rxcui1: f.optional_id(0)?,
rxaui1: f.optional_id(1)?,
rel: f.text(3)?,
rxcui2: f.optional_id(4)?,
rxaui2: f.optional_id(5)?,
rela: f.optional(7)?,
sab: f.text(10)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attribute {
pub rxcui: u64,
pub rxaui: Option<u64>,
pub name: String,
pub sab: String,
pub value: String,
pub suppress: String,
}
impl FromRecord for Attribute {
fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
let f = Fields::new(record, path, line, 13)?;
Ok(Self {
rxcui: f.id(0)?,
rxaui: f.optional_id(3)?,
name: f.text(8)?,
sab: f.text(9)?,
value: f.text(10)?,
suppress: f.text(11)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SemanticType {
pub rxcui: u64,
pub tui: String,
pub name: String,
}
impl FromRecord for SemanticType {
fn from_record(record: &csv::ByteRecord, path: &Path, line: u64) -> Result<Self, RrfError> {
let f = Fields::new(record, path, line, 6)?;
Ok(Self {
rxcui: f.id(0)?,
tui: f.text(1)?,
name: f.text(3)?,
})
}
}