mod entries;
mod index;
mod query;
pub mod package_scope;
pub use self::query::Query;
use self::{entries::Entries, index::Index, package_scope::PackageScope};
use crate::{
advisory::{self, Advisory},
collection::Collection,
error::Error,
lockfile::Lockfile,
repository::{Commit, Repository},
vulnerability::Vulnerability,
};
pub type Iter<'a> = std::slice::Iter<'a, Advisory>;
#[derive(Debug)]
pub struct Database {
advisories: Entries,
rust_index: Index,
crate_index: Index,
latest_commit: Commit,
}
impl Database {
pub fn fetch() -> Result<Self, Error> {
let repo = Repository::fetch_default_repo()?;
Self::load(&repo)
}
pub fn load(repo: &Repository) -> Result<Self, Error> {
let advisory_paths = repo.advisories()?;
let latest_commit = repo.latest_commit()?;
let mut advisories = Entries::new();
let mut rust_index = Index::new();
let mut crate_index = Index::new();
for path in &advisory_paths {
if let Some(slot) = advisories.load_file(path)? {
let advisory = advisories.get(slot).unwrap();
match advisory.metadata.collection.unwrap() {
Collection::Crates => {
crate_index.insert(&advisory.metadata.package, slot);
}
Collection::Rust => {
rust_index.insert(&advisory.metadata.package, slot);
}
}
}
}
Ok(Self {
advisories,
crate_index,
rust_index,
latest_commit,
})
}
pub fn get(&self, id: &advisory::Id) -> Option<&Advisory> {
self.advisories.find_by_id(id)
}
pub fn query(&self, query: &Query) -> Vec<&Advisory> {
if let Some(name) = &query.package {
if let Some(collection) = query.collection {
return match collection {
Collection::Crates => self.crate_index.get(name),
Collection::Rust => self.rust_index.get(name),
}
.map(|slots| {
slots
.map(|slot| self.advisories.get(*slot).unwrap())
.filter(|advisory| query.matches(advisory))
.collect()
})
.unwrap_or_else(Vec::new);
}
}
self.iter()
.filter(|advisory| query.matches(advisory))
.collect()
}
pub fn query_vulnerabilities(
&self,
lockfile: &Lockfile,
query: &Query,
scope: &PackageScope,
) -> Vec<Vulnerability> {
let mut vulns = vec![];
for package in &lockfile.packages {
if scope.is_no_local() && package.source.is_none() {
continue;
}
let advisories = self.query(
&query
.clone()
.package_version(package.name.clone(), package.version.clone()),
);
vulns.extend(
advisories
.iter()
.map(|advisory| Vulnerability::new(advisory, package)),
);
}
vulns
}
pub fn vulnerabilities(&self, lockfile: &Lockfile) -> Vec<Vulnerability> {
self.query_vulnerabilities(lockfile, &Query::crate_scope(), &PackageScope::default())
}
pub fn iter(&self) -> Iter<'_> {
self.advisories.iter()
}
pub fn latest_commit(&self) -> &Commit {
&self.latest_commit
}
}