use std::collections::BTreeMap;
use crate::id;
use crate::release::{Release, ReleaseError, TERMS, Table, csv_at, field};
pub const ACTIVE: &str = "ACTIVE";
pub const DEPRECATED: &str = "DEPRECATED";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Term {
pub code: String,
pub long_common_name: String,
pub short_name: String,
pub consumer_name: String,
pub status: String,
pub external_copyright: Option<String>,
pub fields: BTreeMap<String, String>,
}
impl Term {
#[must_use]
pub fn active(&self) -> bool {
!self.status.eq_ignore_ascii_case(DEPRECATED)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Terms {
pub columns: Vec<String>,
pub rows: Vec<Term>,
}
pub fn read(release: &Release) -> Result<Terms, ReleaseError> {
let mut table = Table::open(release.file(TERMS)?)?;
let code_at = table.column("LOINC_NUM")?;
let columns = table.columns();
let at = |name: &str| columns.iter().position(|c| c.eq_ignore_ascii_case(name));
let long_at = at("LONG_COMMON_NAME");
let short_at = at("SHORTNAME");
let consumer_at = at("CONSUMER_NAME");
let status_at = at("STATUS");
let copyright_at = at("EXTERNAL_COPYRIGHT_NOTICE");
let mut rows = Vec::new();
let path = table.path.clone();
for record in table.reader.records() {
let record = record.map_err(|e| csv_at(&path, e))?;
let code = field(&record, code_at).to_owned();
if !id::is_well_formed(&code) {
return Err(ReleaseError::Code {
path: table.path.clone(),
code,
});
}
let text = |at: Option<usize>| at.map(|i| field(&record, i).to_owned()).unwrap_or_default();
let mut fields = BTreeMap::new();
for (index, column) in columns.iter().enumerate() {
let value = field(&record, index);
if !value.is_empty() {
fields.insert(column.clone(), value.to_owned());
}
}
rows.push(Term {
code,
long_common_name: text(long_at),
short_name: text(short_at),
consumer_name: text(consumer_at),
status: text(status_at),
external_copyright: copyright_at
.map(|i| field(&record, i))
.filter(|v| !v.is_empty())
.map(str::to_owned),
fields,
});
}
Ok(Terms { columns, rows })
}