use super::Iter;
use crate::{
advisory::{self, Advisory},
collection::Collection,
error::{Error, ErrorKind},
map, Map,
};
use std::{
ffi::{OsStr, OsString},
path::Path,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) struct Slot(usize);
#[derive(Debug, Default)]
pub(crate) struct Entries {
index: Map<advisory::Id, Slot>,
advisories: Vec<Advisory>,
}
impl Entries {
pub fn new() -> Self {
Self::default()
}
pub fn load_file(&mut self, path: &Path) -> Result<Option<Slot>, Error> {
let mut advisory = Advisory::load_file(path)?;
let expected_filename = OsString::from(format!("{}.toml", advisory.metadata.id));
if path.file_name().unwrap() != expected_filename {
fail!(
ErrorKind::Repo,
"expected {} to be named {:?}",
path.display(),
expected_filename
);
}
let package_dir = path.parent().ok_or_else(|| {
format_err!(
ErrorKind::Repo,
"advisory has no parent dir: {}",
path.display()
)
})?;
if package_dir.file_name().unwrap() != OsStr::new(advisory.metadata.package.as_str()) {
fail!(
ErrorKind::Repo,
"expected {} to be in {} directory (instead of \"{:?}\")",
advisory.metadata.id,
advisory.metadata.package,
package_dir
);
}
let collection_dir = package_dir
.parent()
.ok_or_else(|| {
format_err!(
ErrorKind::Repo,
"advisory has no collection: {}",
path.display()
)
})?
.file_name()
.unwrap();
let collection = if collection_dir == OsStr::new(Collection::Crates.as_str()) {
Collection::Crates
} else if collection_dir == OsStr::new(Collection::Rust.as_str()) {
Collection::Rust
} else {
fail!(
ErrorKind::Repo,
"invalid package collection: {:?}",
collection_dir
);
};
match advisory.metadata.collection {
Some(c) => {
if c != collection {
fail!(
ErrorKind::Parse,
"collection mismatch for {}",
&advisory.metadata.id
);
}
}
None => advisory.metadata.collection = Some(collection),
}
if advisory.metadata.id.is_placeholder() {
return Ok(None);
}
let id = advisory.metadata.id.clone();
let slot = Slot(self.advisories.len());
self.advisories.push(advisory);
match self.index.entry(id) {
map::Entry::Vacant(entry) => {
entry.insert(slot);
}
map::Entry::Occupied(entry) => {
fail!(ErrorKind::Parse, "duplicate advisory ID: {}", entry.key())
}
}
Ok(Some(slot))
}
pub fn find_by_id(&self, id: &advisory::Id) -> Option<&Advisory> {
self.index.get(id).and_then(|slot| self.get(*slot))
}
pub fn get(&self, slot: Slot) -> Option<&Advisory> {
self.advisories.get(slot.0)
}
pub fn iter(&self) -> Iter<'_> {
self.advisories.iter()
}
}