blob: e046efa1f06846e422de96aa809c342ad24e470d [file] [log] [blame]
use std::{
ffi::OsString,
fmt::{Debug, Display},
num::ParseIntError,
path::{PathBuf, StripPrefixError},
string::FromUtf8Error,
sync::Arc,
};
use cargo_metadata::semver;
use miette::{Diagnostic, MietteSpanContents, SourceCode, SourceOffset, SourceSpan};
use thiserror::Error;
use crate::{
format::{
CriteriaName, ForeignCriteriaName, ImportName, PackageName, StoreVersion, VetVersion,
},
network::PayloadEncoding,
serialization::spanned::Spanned,
};
#[derive(Eq, PartialEq)]
struct SourceFileInner {
name: String,
source: String,
}
#[derive(Clone, Eq, PartialEq)]
pub struct SourceFile {
inner: Arc<SourceFileInner>,
}
impl SourceFile {
pub fn new_empty(name: &str) -> Self {
Self::new(name, String::new())
}
pub fn new(name: &str, source: String) -> Self {
SourceFile {
inner: Arc::new(SourceFileInner {
name: name.to_owned(),
source,
}),
}
}
pub fn name(&self) -> &str {
&self.inner.name
}
pub fn source(&self) -> &str {
&self.inner.source
}
}
impl SourceCode for SourceFile {
fn read_span<'a>(
&'a self,
span: &SourceSpan,
context_lines_before: usize,
context_lines_after: usize,
) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
let contents = self
.source()
.read_span(span, context_lines_before, context_lines_after)?;
Ok(Box::new(MietteSpanContents::new_named(
self.name().to_owned(),
contents.data(),
*contents.span(),
contents.line(),
contents.column(),
contents.line_count(),
)))
}
}
impl Debug for SourceFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SourceFile")
.field("name", &self.name())
.field("source", &self.source())
.finish()
}
}
//////////////////////////////////////////////////////////
// VersionParseError
//////////////////////////////////////////////////////////
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VersionParseError {
#[error(transparent)]
Semver(#[from] semver::Error),
#[error("unrecognized revision type, expected 'git:' prefix")]
UnknownRevision,
#[error("unrecognized git hash, expected 40 hex digits")]
InvalidGitHash,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum StoreVersionParseError {
#[error("error parsing version component: {0}")]
ParseInt(#[from] ParseIntError),
#[error("missing '.' separator")]
MissingSeparator,
}
///////////////////////////////////////////////////////////
// MetadataAcquireError
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
pub enum MetadataAcquireError {
#[error("`cargo metadata` exited with an error:\n{stderr}")]
#[diagnostic(help("You may need to run `cargo generate-lockfile` to create a Cargo.lock"))]
MetadataError {
/// Stderr returned by the `cargo metadata` command
stderr: String,
},
#[error(transparent)]
Other(cargo_metadata::Error),
}
impl From<cargo_metadata::Error> for MetadataAcquireError {
fn from(value: cargo_metadata::Error) -> Self {
match value {
// Wrap normal errors to provide extra help information.
cargo_metadata::Error::CargoMetadata { stderr } => {
MetadataAcquireError::MetadataError { stderr }
}
// Other errors couldn't be caused by that problem, so are
// unchanged.
other => MetadataAcquireError::Other(other),
}
}
}
///////////////////////////////////////////////////////////
// AuditAsErrors
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[error("There are some issues with your policy.audit-as-crates-io entries")]
#[diagnostic()]
pub struct AuditAsErrors {
#[related]
pub errors: Vec<AuditAsError>,
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum AuditAsError {
#[error(transparent)]
#[diagnostic(transparent)]
NeedsAuditAs(NeedsAuditAsErrors),
#[error(transparent)]
#[diagnostic(transparent)]
ShouldntBeAuditAs(ShouldntBeAuditAsErrors),
#[error(transparent)]
#[diagnostic(transparent)]
UnusedAuditAs(UnusedAuditAsErrors),
}
#[derive(Debug, Error, Diagnostic)]
#[diagnostic(help("Add a `policy.*.audit-as-crates-io` entry for them"))]
pub struct NeedsAuditAsErrors {
pub errors: Vec<PackageError>,
}
impl Display for NeedsAuditAsErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Some non-crates.io-fetched packages match published crates.io versions")?;
for e in &self.errors {
f.write_fmt(format_args!("\n {e}"))?
}
Ok(())
}
}
#[derive(Debug, Error, Diagnostic)]
#[diagnostic(help("Remove the audit-as-crates-io entries or make them `false`"))]
pub struct ShouldntBeAuditAsErrors {
pub errors: Vec<PackageError>,
}
impl Display for ShouldntBeAuditAsErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("some audit-as-crates-io packages don't match published crates.io versions")?;
for e in &self.errors {
f.write_fmt(format_args!("\n {e}"))?
}
Ok(())
}
}
#[derive(Debug, Error, Diagnostic)]
#[diagnostic(help("Remove the audit-as-crates-io entries"))]
pub struct UnusedAuditAsErrors {
pub errors: Vec<PackageError>,
}
impl Display for UnusedAuditAsErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("some audit-as-crates-io policies don't match first-party crates")?;
for e in &self.errors {
f.write_fmt(format_args!("\n {e}"))?
}
Ok(())
}
}
#[derive(Debug, Error, PartialEq, Eq)]
#[error("{package}{}", .version.as_ref().map(|v| format!(":{v}")).unwrap_or_default())]
pub struct PackageError {
pub package: PackageName,
pub version: Option<VetVersion>,
}
///////////////////////////////////////////////////////////
// CratePolicyErrors
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic, PartialEq, Eq)]
#[error("There are some issues with your third-party policy entries")]
#[diagnostic()]
pub struct CratePolicyErrors {
#[related]
pub errors: Vec<CratePolicyError>,
}
#[derive(Debug, Error, Diagnostic, PartialEq, Eq)]
#[non_exhaustive]
pub enum CratePolicyError {
#[error(transparent)]
#[diagnostic(transparent)]
NeedsVersion(NeedsPolicyVersionErrors),
#[error(transparent)]
#[diagnostic(transparent)]
UnusedVersion(UnusedPolicyVersionErrors),
}
#[derive(Debug, Error, Diagnostic, PartialEq, Eq)]
#[diagnostic(help(
"Specifing `dependency-criteria` requires explicit policies for each version of \
a crate. Add a `policy.\"<crate>:<version>\"` entry for them."
))]
pub struct NeedsPolicyVersionErrors {
pub errors: Vec<PackageError>,
}
impl Display for NeedsPolicyVersionErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("some crates have policies that are missing an associated version")?;
for e in &self.errors {
f.write_fmt(format_args!("\n {e}"))?
}
Ok(())
}
}
#[derive(Debug, Error, Diagnostic, PartialEq, Eq)]
#[diagnostic(help("Remove the `policy` entries"))]
pub struct UnusedPolicyVersionErrors {
pub errors: Vec<PackageError>,
}
impl Display for UnusedPolicyVersionErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("some versioned policy entries don't correspond to crates being used")?;
for e in &self.errors {
f.write_fmt(format_args!("\n {e}"))?
}
Ok(())
}
}
///////////////////////////////////////////////////////////
// CertifyError
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum CertifyError {
#[error("no criteria chosen, aborting")]
NoCriteriaChosen,
#[error("couldn't guess what version of {0} to certify, please specify")]
CouldntGuessVersion(PackageName),
#[error("couldn't guess what package to certify, please specify")]
CouldntGuessPackage,
#[error("couldn't find uncommented certify statement")]
CouldntFindCertifyStatement,
#[error("'{0}' isn't one of your foreign packages")]
#[diagnostic(help("use --force to ignore this error"))]
NotAPackage(PackageName),
#[error("'{0}' has not published any relevant version of '{1}'")]
#[diagnostic(help("please specify a user who has published a version of '{1}'"))]
NotAPublisher(String, PackageName),
#[error("end date of {0} is too far in the future")]
#[diagnostic(help("wildcard audit end dates may be at most 1 year in the future"))]
BadWildcardEndDate(chrono::NaiveDate),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
EditError(#[from] EditError),
#[error(transparent)]
UserInfoError(#[from] UserInfoError),
#[error(transparent)]
FetchAuditError(#[from] FetchAuditError),
#[error(transparent)]
GetPublishersError(#[from] CrateInfoError),
#[error(transparent)]
CacheAcquire(#[from] CacheAcquireError),
}
///////////////////////////////////////////////////////////
// EditError
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum EditError {
#[error("Failed to launch editor")]
CouldntLaunch(#[source] std::io::Error),
#[error("Failed to open result of editor")]
CouldntOpen(#[source] std::io::Error),
#[error("Failed to read result of editor")]
CouldntRead(#[source] std::io::Error),
}
///////////////////////////////////////////////////////////
// InitErrors
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[error("Failed to initialize the cargo-vet store (supply-chain)")]
#[non_exhaustive]
pub enum InitError {
StoreCreate(#[source] StoreCreateError),
StoreCommit(#[source] StoreCommitError),
}
///////////////////////////////////////////////////////////
// StoreErrors
///////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
#[error("Couldn't create the store (supply-chain)")]
pub enum StoreCreateError {
CouldntCreate(#[source] std::io::Error),
CouldntAcquire(
#[from]
#[source]
FlockError,
),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum StoreAcquireError {
#[error("Couldn't acquire the store's (supply-chain's) lock")]
CouldntLock(
#[from]
#[source]
FlockError,
),
#[error("The supply-chain store was created with an incompatible version of cargo-vet ({0})")]
#[help("Run cargo vet without --locked to update the store to this version")]
OutdatedStore(StoreVersion),
#[error("The supply-chain store was created with a newer version of cargo-vet ({0})")]
#[help("Update to the latest version using `cargo install cargo-vet`")]
NewerStore(StoreVersion),
#[error(transparent)]
#[diagnostic(transparent)]
LoadToml(#[from] LoadTomlError),
#[error("Couldn't acquire the store")]
IoError(
#[from]
#[source]
std::io::Error,
),
#[error(transparent)]
#[diagnostic(transparent)]
Validate(#[from] StoreValidateErrors),
#[error(transparent)]
#[diagnostic(transparent)]
FetchAuditError(#[from] Box<FetchAuditError>),
#[error(transparent)]
#[diagnostic(transparent)]
GetPublishersError(#[from] Box<CrateInfoError>),
#[diagnostic(transparent)]
#[error(transparent)]
CriteriaChange(#[from] CriteriaChangeErrors),
#[diagnostic(transparent)]
#[error(transparent)]
CacheAcquire(#[from] Box<CacheAcquireError>),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
#[error("Failed to commit store")]
pub enum StoreCommitError {
IoError(
#[from]
#[source]
std::io::Error,
),
StoreToml(
#[from]
#[source]
StoreTomlError,
),
}
#[derive(Debug, Error, Diagnostic)]
#[error("Some of your imported audits changed their criteria descriptions")]
#[diagnostic()]
pub struct CriteriaChangeErrors {
#[related]
pub errors: Vec<CriteriaChangeError>,
}
#[derive(Debug, Error, Diagnostic)]
#[error("{import_name}'s '{criteria_name}' criteria changed:\n\n{unified_diff}")]
#[diagnostic(help("Run `cargo vet regenerate imports` to accept this new definition"))]
pub struct CriteriaChangeError {
pub import_name: ImportName,
pub criteria_name: ForeignCriteriaName,
pub unified_diff: String,
}
////////////////////////////////////////////////////////////
// StoreValidateErrors
////////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[error("Your cargo-vet store (supply-chain) has consistency errors")]
pub struct StoreValidateErrors {
#[related]
pub errors: Vec<StoreValidateError>,
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum StoreValidateError {
#[diagnostic(transparent)]
#[error(transparent)]
InvalidCriteria(InvalidCriteriaError),
#[diagnostic(transparent)]
#[error(transparent)]
BadFormat(BadFormatError),
#[diagnostic(transparent)]
#[error(transparent)]
BadWildcardEndDate(BadWildcardEndDateError),
#[error("imports.lock is out-of-date with respect to configuration")]
#[diagnostic(help("run `cargo vet` without --locked to update imports"))]
ImportsLockOutdated,
}
#[derive(Debug, Error, Diagnostic)]
#[error("'{invalid}' is not a valid criteria name")]
#[diagnostic(help("the possible criteria are {:?}", valid_names))]
pub struct InvalidCriteriaError {
#[source_code]
pub source_code: SourceFile,
#[label]
pub span: SourceSpan,
pub invalid: String,
pub valid_names: Arc<Vec<String>>,
}
#[derive(Debug, Error, Diagnostic)]
#[error("A file in the store is not correctly formatted:\n\n{unified_diff}")]
#[diagnostic(help("run `cargo vet` without --locked to reformat files in the store"))]
pub struct BadFormatError {
pub unified_diff: String,
}
#[derive(Debug, Error, Diagnostic)]
#[error("'{date}' is more than a year in the future")]
#[diagnostic(help("wildcard audits must end at most a year in the future ({max})"))]
pub struct BadWildcardEndDateError {
#[source_code]
pub source_code: SourceFile,
#[label]
pub span: SourceSpan,
pub date: chrono::NaiveDate,
pub max: chrono::NaiveDate,
}
//////////////////////////////////////////////////////////
// CacheErrors
/////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum CacheAcquireError {
#[error("Couldn't acquire cache")]
IoError(
#[from]
#[source]
std::io::Error,
),
#[error("Failed to create cache root dir: {}", target.display())]
Root {
target: PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to create cache src dir: {}", target.display())]
Src {
target: PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to create cache empty dir: {}", target.display())]
Empty {
target: PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to create cache package dir: {}", target.display())]
Cache {
target: PathBuf,
#[source]
error: std::io::Error,
},
#[error("Couldn't acquire the cache's lock")]
CouldntLock(
#[from]
#[source]
FlockError,
),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
#[error("Failed to commit cache")]
pub enum CacheCommitError {
IoError(
#[from]
#[source]
std::io::Error,
),
StoreToml(
#[from]
#[source]
StoreTomlError,
),
StoreJson(
#[from]
#[source]
StoreJsonError,
),
}
//////////////////////////////////////////////////////////
/// CommandError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum CommandError {
#[error("Command failed")]
CommandFailed(#[source] std::io::Error),
#[error("Bad status {0}")]
BadStatus(i32),
#[error("Wasn't UTF-8")]
BadOutput(#[source] FromUtf8Error),
}
//////////////////////////////////////////////////////////
// FetchAndDiffError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum FetchAndDiffError {
#[error(transparent)]
#[diagnostic(transparent)]
Diff(#[from] DiffError),
#[error(transparent)]
#[diagnostic(transparent)]
Fetch(#[from] FetchError),
}
//////////////////////////////////////////////////////////
// DiffError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum DiffError {
#[error("Failed to diff package")]
CommandError(
#[from]
#[source]
CommandError,
),
#[error("Diff command produced an unexpected path")]
UnexpectedPath(#[source] StripPrefixError),
#[error("Diff command produced invalid output")]
InvalidOutput,
}
//////////////////////////////////////////////////////////
// UserInfoError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum UserInfoError {
#[error("Failed to get user.name")]
UserCommandFailed(#[source] CommandError),
#[error("Failed to get user.email")]
EmailCommandFailed(#[source] CommandError),
}
//////////////////////////////////////////////////////////
// FetchError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum FetchError {
#[error("Invalid URL for package: {url}")]
InvalidUrl {
url: String,
#[source]
error: url::ParseError,
},
#[error("Running as --frozen but needed to fetch {package}:{version}")]
Frozen {
package: PackageName,
version: semver::Version,
},
#[error("Failed to unpack .crate at {}", src.display())]
Unpack {
src: PathBuf,
#[source]
error: UnpackError,
},
#[error("failed to open cached .crate at {}", target.display())]
OpenCached {
target: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to unpack checkout at {}", src.display())]
UnpackCheckout {
src: PathBuf,
#[source]
error: UnpackCheckoutError,
},
#[error("Cannot get source for unknown git commit {git_rev} of {package}")]
#[help("Only revisions actively used in the dependency graph can be located")]
UnknownGitRevision {
package: PackageName,
git_rev: String,
},
#[error(transparent)]
#[diagnostic(transparent)]
Download(#[from] DownloadError),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum UnpackError {
#[error("Failed to iterate archive")]
ArchiveIterate(#[source] std::io::Error),
#[error("Failed to read archive entry")]
ArchiveEntry(#[source] std::io::Error),
#[error("Invalid archive, {} wasn't under {}", entry_path.display(), prefix.to_string_lossy())]
InvalidPaths {
entry_path: PathBuf,
prefix: OsString,
},
#[error("Failed to unpack archive entry {}", entry_path.display())]
Unpack {
entry_path: PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to finalize unpack to {}", target.display())]
LockCreate {
target: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error(transparent)]
IoError(#[from] std::io::Error),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum UnpackCheckoutError {
#[error("Failed to run 'cargo package --list'")]
CommandError(
#[from]
#[source]
CommandError,
),
#[error("Failed to create directory {path}")]
CreateDirError {
path: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to copy file contents for {target}")]
CopyError {
target: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("Failed to finalize checkout unpack")]
LockCreate(#[source] std::io::Error),
}
//////////////////////////////////////////////////////////
// FetchAuditError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum FetchAuditError {
// FIXME: would have to explicitly import URL for this error
#[error("invalid URL for foreign import {import_name} @ {import_url}")]
InvalidUrl {
import_name: ImportName,
import_url: String,
#[source]
error: url::ParseError,
},
#[error("{import_name}'s '{criteria_name}' criteria is missing a description")]
MissingCriteriaDescription {
import_name: ImportName,
criteria_name: ForeignCriteriaName,
},
#[error("{import_name}'s '{criteria_name}' criteria description URI is invalid: '{url}'")]
InvalidCriteriaDescriptionUrl {
import_name: ImportName,
criteria_name: ForeignCriteriaName,
url: String,
#[source]
error: url::ParseError,
},
#[error("error when aggregating multiple sources for {import_name}")]
#[diagnostic(help("all sources for mapped custom criteria must have identical descriptions"))]
Aggregate {
import_name: ImportName,
#[related]
errors: Vec<FetchAuditAggregateError>,
},
#[diagnostic(transparent)]
#[error(transparent)]
Download(#[from] DownloadError),
#[diagnostic(transparent)]
#[error(transparent)]
Toml(#[from] LoadTomlError),
#[diagnostic(transparent)]
#[error(transparent)]
Json(#[from] LoadJsonError),
}
#[derive(Debug, Error, Diagnostic)]
#[error("criteria description mismatch for {criteria_name}\n{first}\n{second}")]
#[diagnostic(help("{criteria_name} is mapped to the local criteria {mapped_to:?}"))]
pub struct FetchAuditAggregateError {
pub criteria_name: CriteriaName,
pub mapped_to: Vec<Spanned<CriteriaName>>,
pub first: AggregateCriteriaDescription,
pub second: AggregateCriteriaDescription,
}
//////////////////////////////////////////////////////////
// CrateInfoError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum CrateInfoError {
#[diagnostic(transparent)]
#[error(transparent)]
Download(#[from] DownloadError),
#[diagnostic(transparent)]
#[error(transparent)]
Json(#[from] LoadJsonError),
#[error("Cannot fetch crate information, '{name}' does not exist.")]
DoesNotExist { name: PackageName },
}
//////////////////////////////////////////////////////////
// FetchRegistryError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum FetchRegistryError {
#[error("Encountered an error fetching the cargo-vet registry.")]
Download(#[from] DownloadError),
#[error("Import suggestions are disabled due to an incompatible registry. Consider upgrading to the most recent release of cargo-vet.")]
Toml(#[from] LoadTomlError),
#[error("Error when fetching crate information. Registry suggestions may be incomplete.")]
CrateInfo(#[from] CrateInfoError),
}
//////////////////////////////////////////////////////////
// DownloadError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum DownloadError {
#[error("failed to start download of {url}")]
FailedToStartDownload {
url: Box<reqwest::Url>,
#[source]
error: reqwest::Error,
},
#[error("failed to create file for download to {}", target.display())]
FailedToCreateDownload {
target: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("failed to read download from {url}")]
FailedToReadDownload {
url: Box<reqwest::Url>,
#[source]
error: reqwest::Error,
},
#[error("failed to write download to {}", target.display())]
FailedToWriteDownload {
target: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("failed to rename download to final location {}", target.display())]
FailedToFinalizeDownload {
target: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("download wasn't valid utf8: {url}")]
InvalidText {
url: Box<reqwest::Url>,
#[source]
error: FromUtf8Error,
},
#[error("download encoding ({encoding}) error")]
InvalidEncoding {
encoding: PayloadEncoding,
#[source]
error: std::io::Error,
},
}
//////////////////////////////////////////////////////////
// SuggestError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum SuggestError {
#[error(transparent)]
#[diagnostic(transparent)]
CacheAcquire(#[from] CacheAcquireError),
#[error(transparent)]
#[diagnostic(transparent)]
FetchAudit(#[from] FetchAuditError),
}
//////////////////////////////////////////////////////////
// FlockError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum FlockError {
#[error("couldn't acquire file lock")]
IoError(
#[from]
#[source]
std::io::Error,
),
}
//////////////////////////////////////////////////////////
// AggregateErrors
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[error("there were errors aggregating source audit files")]
#[diagnostic()]
pub struct AggregateErrors {
#[related]
pub errors: Vec<AggregateError>,
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum AggregateError {
#[error(transparent)]
#[diagnostic(transparent)]
CriteriaDescriptionMismatch(AggregateCriteriaDescriptionMismatchError),
#[error(transparent)]
#[diagnostic(transparent)]
ImpliesMismatch(AggregateImpliesMismatchError),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
#[error("criteria description mismatch for {criteria_name}\n{first}\n{second}")]
pub struct AggregateCriteriaDescriptionMismatchError {
pub criteria_name: CriteriaName,
pub first: AggregateCriteriaDescription,
pub second: AggregateCriteriaDescription,
}
#[derive(Debug)]
pub struct AggregateCriteriaDescription {
pub source: String,
pub description: Option<String>,
pub description_url: Option<String>,
}
impl Display for AggregateCriteriaDescription {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(description) = &self.description {
write!(f, "{}:\n{}", self.source, description)
} else if let Some(description_url) = &self.description_url {
write!(f, "{}:\n(URL) {}", self.source, description_url)
} else {
write!(f, "{}:\n(no description)", self.source)
}
}
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
#[error("implied criteria mismatch for {criteria_name}\n{first}\n{second}")]
pub struct AggregateImpliesMismatchError {
pub criteria_name: CriteriaName,
pub first: AggregateCriteriaImplies,
pub second: AggregateCriteriaImplies,
}
#[derive(Debug)]
pub struct AggregateCriteriaImplies {
pub source: String,
pub implies: Vec<CriteriaName>,
}
impl Display for AggregateCriteriaImplies {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}:", self.source)?;
for implied in &self.implies {
write!(f, "\n - {implied}")?;
}
Ok(())
}
}
//////////////////////////////////////////////////////////
// TomlError/JsonError
//////////////////////////////////////////////////////////
#[derive(Debug, Error, Diagnostic)]
#[error("Failed to parse toml file")]
pub struct TomlParseError {
#[source_code]
pub source_code: SourceFile,
#[label("here")]
pub span: SourceOffset,
#[source]
pub error: toml::de::Error,
}
#[derive(Debug, Error, Diagnostic)]
#[error("Failed to parse json file")]
pub struct JsonParseError {
// #[source_code]
// input: SourceFile,
// #[label("here")]
// span: SourceOffset,
#[source]
pub error: serde_json::Error,
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum LoadTomlError {
#[error(transparent)]
#[diagnostic(transparent)]
TomlParse(#[from] TomlParseError),
#[error("TOML wasn't valid utf8")]
InvalidText {
#[source]
#[from]
error: FromUtf8Error,
},
#[error("couldn't load toml")]
IoError(
#[from]
#[source]
std::io::Error,
),
}
#[derive(Debug, Error, Diagnostic)]
#[non_exhaustive]
pub enum LoadJsonError {
#[error(transparent)]
#[diagnostic(transparent)]
JsonParse(#[from] JsonParseError),
#[error("couldn't load json")]
IoError(
#[from]
#[source]
std::io::Error,
),
}
pub type StoreJsonError = serde_json::Error;
pub type StoreTomlError = toml_edit::ser::Error;