mod compare;
mod diff;
mod error;
mod format;
mod krate;
mod metadata;
mod report;
mod severity;
mod upstream;
mod utils;
mod vcsinfo;
mod workarounds;
pub use crate::diff::{Diff, DiffItem};
pub use crate::error::Error;
pub use crate::krate::Crate;
pub use crate::report::{Report, ReportItem};
pub use crate::severity::Severity;
use crate::compare::{CrateComparator, RepoComparator};
use crate::upstream::Repository;
use crate::vcsinfo::vcs_info_from_root;
use crate::workarounds::sanitize_repo_url;
pub use serde_json::Value;
#[non_exhaustive]
#[derive(Clone, Default)]
pub struct ReportOptions {
pub repository: Option<String>,
pub path_in_vcs: Option<String>,
pub vcs_ref: Option<String>,
}
impl ReportOptions {
#[must_use]
pub const fn new() -> Self {
ReportOptions {
repository: None,
path_in_vcs: None,
vcs_ref: None,
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.repository.is_none() && self.path_in_vcs.is_none() && self.vcs_ref.is_none()
}
}
impl Crate {
pub async fn report(&self) -> Result<Report, Error> {
let mut items = Vec::new();
let Some(vcs_info) = vcs_info_from_root(&self.root)? else {
items.push(ReportItem::MissingVcsInfo);
return Ok(Report::from_items(items));
};
if vcs_info.git.dirty {
items.push(ReportItem::DirtyRepository);
}
let Some(path_in_vcs) = vcs_info.path_in_vcs.as_ref() else {
items.push(ReportItem::NoPathInVcsInfo);
return Ok(Report::from_items(items));
};
let Some(url) = &self.metadata.inner.repository else {
items.push(ReportItem::MissingRepositoryUrl);
return Ok(Report::from_items(items));
};
let sanitized_url = sanitize_repo_url(url)?;
let upstream = match Repository::clone(
&sanitized_url,
&vcs_info.git.sha1,
path_in_vcs,
&self.metadata.inner.name,
)
.await
{
Ok(repo) => repo,
Err(err) => match err {
Error::InvalidGitRef { repo, rev } => {
items.push(ReportItem::InvalidGitRef { repo, rev });
return Ok(Report::from_items(items));
},
Error::InvalidRepoUrl { repo } => {
items.push(ReportItem::InvalidRepoUrl { repo });
return Ok(Report::from_items(items));
},
_ => return Err(err),
},
};
let comparator = RepoComparator::new(self, &upstream);
items.extend(comparator.compare()?);
Ok(Report::from_items(items))
}
pub async fn report_with_options(&self, options: ReportOptions) -> Result<Report, Error> {
let mut items = Vec::new();
let (path_in_vcs, vcs_ref) = if let Some(vcs_info) = vcs_info_from_root(&self.root)? {
if vcs_info.git.dirty {
items.push(ReportItem::DirtyRepository);
}
let Some(path_in_vcs) = options.path_in_vcs.or(vcs_info.path_in_vcs) else {
items.push(ReportItem::NoPathInVcsInfo);
return Ok(Report::from_items(items));
};
let vcs_ref = options.vcs_ref.unwrap_or(vcs_info.git.sha1);
(path_in_vcs, vcs_ref)
} else {
items.push(ReportItem::MissingVcsInfo);
let Some(path_in_vcs) = options.path_in_vcs else {
items.push(ReportItem::NoPathInVcsInfo);
return Ok(Report::from_items(items));
};
let Some(vcs_ref) = options.vcs_ref else {
return Ok(Report::from_items(items));
};
(path_in_vcs, vcs_ref)
};
let Some(url) = options.repository.or(self.metadata.inner.repository.clone()) else {
items.push(ReportItem::MissingRepositoryUrl);
return Ok(Report::from_items(items));
};
let sanitized_url = sanitize_repo_url(&url)?;
let upstream = match Repository::clone(&sanitized_url, &vcs_ref, &path_in_vcs, &self.metadata.inner.name).await
{
Ok(repo) => repo,
Err(err) => match err {
Error::InvalidGitRef { repo, rev } => {
items.push(ReportItem::InvalidGitRef { repo, rev });
return Ok(Report::from_items(items));
},
Error::InvalidRepoUrl { repo } => {
items.push(ReportItem::InvalidRepoUrl { repo });
return Ok(Report::from_items(items));
},
_ => return Err(err),
},
};
let comparator = RepoComparator::new(self, &upstream);
items.extend(comparator.compare()?);
Ok(Report::from_items(items))
}
pub fn diff(&self, other: &Crate) -> Result<Diff, Error> {
let comparator = CrateComparator::new(self, other);
Ok(Diff::from_items(comparator.compare()?))
}
}