use super::{presets, Attribute};
use super::parser::Entry;
use regex::bytes::{RegexSet, RegexSetBuilder};
use std::collections::HashSet;
use ata::data::id;
#[derive(Debug)]
pub struct DriveDB {
entries: Vec<Entry>,
default: Option<Entry>,
model_regexes: RegexSet,
firmware_regexes: RegexSet,
}
impl DriveDB {
pub(crate) fn new(entries: Vec<Entry>) -> Self {
let entries = entries.into_iter()
.filter(|e| ! e.model.starts_with("USB:"));
let (default, entries): (Vec<_>, Vec<_>) = entries.partition(|e| e.family == "DEFAULT");
let default = default.into_iter().next();
let model_regexes = RegexSetBuilder::new(entries.iter()
.map(|e| format!("^{}$", e.model))
).unicode(false).build().unwrap();
let firmware_regexes = RegexSetBuilder::new(entries.iter()
.map(|e|
if e.firmware.is_empty() {
"".to_string()
} else {
format!("^{}$", e.firmware)
}
)
).unicode(false).build().unwrap();
DriveDB {
entries,
default,
model_regexes,
firmware_regexes,
}
}
fn find(&self, model: &str, firmware: &str) -> Option<&Entry> {
let models: HashSet<_> = self.model_regexes.matches(model.as_bytes()).iter().collect();
let firmwares: HashSet<_> = self.firmware_regexes.matches(firmware.as_bytes()).iter().collect();
models.intersection(&firmwares)
.min()
.map(|index| &self.entries[*index])
}
pub fn render_meta(&self, id: &id::Id, extra_attributes: &Vec<Attribute>) -> DriveMeta {
let mut m = DriveMeta {
family: None,
warning: None,
presets: Vec::<Attribute>::new(),
};
if let Some(ref default) = self.default {
if let Some(presets) = presets::parse(&default.presets) {
m.presets.extend(presets);
}
}
if let Some(entry) = self.find(&id.model, &id.firmware) {
if let Some(presets) = presets::parse(&entry.presets) {
m.presets.extend(presets);
}
m.family = Some(&entry.family);
m.warning = if ! entry.warning.is_empty() { Some(&entry.warning) } else { None };
}
m.presets.extend(extra_attributes.iter().map(|a| a.clone()));
m.presets = filter_presets(id, m.presets);
return m;
}
}
fn filter_presets(id: &id::Id, preset: Vec<Attribute>) -> Vec<Attribute> {
let drivetype = {
use self::id::RPM::*;
use super::vendor_attribute::Type::*;
match id.rpm {
RPM(_) => Some(HDD),
NonRotating => Some(SSD),
Unknown => None,
}
};
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
preset.into_iter().filter(|attr| match (&attr.drivetype, &drivetype) {
(&None, _) => true,
(&Some(ref a), &Some(ref b)) if a == b => true,
(&Some(_), &Some(_)) => false,
(&Some(_), &None) => false,
}).collect()
}
#[derive(Debug)]
pub struct DriveMeta<'a> {
pub family: Option<&'a String>,
pub warning: Option<&'a String>,
presets: Vec<Attribute>,
}
impl<'a> DriveMeta<'a> {
pub fn render_attribute(&'a self, id: u8) -> Option<Attribute> {
let mut out = None;
for new in self.presets.iter() {
match new.id {
Some(x) if x != id => continue,
_ => ()
}
match out {
None => { out = Some(new.clone()); },
Some(ref mut old) => {
old.format = new.format.clone();
old.byte_order = new.byte_order.clone();
if new.name.is_some() {
old.name = new.name.clone();
}
if new.drivetype.is_some() {
old.drivetype = new.drivetype;
}
},
}
}
out
}
}