use std::path::PathBuf;
use thiserror::Error;
use crate::model::{ProjectId, VarId, VarKind};
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum MetadataError {
#[error("variable named {0:?} already exists")]
DuplicateName(String),
#[error("variable {0} not found")]
VarNotFound(VarId),
#[error("project {0} not found")]
ProjectNotFound(ProjectId),
#[error("invalid input: {0}")]
Invalid(String),
#[error("variable {id} has kind {actual:?} but {expected:?} was required")]
KindMismatch {
id: VarId,
expected: VarKind,
actual: VarKind,
},
#[error("backend error: {0}")]
Backend(String),
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum SecretError {
#[error("secret backend error: {0}")]
Backend(String),
#[error("no secure storage available on this platform")]
Unavailable,
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum ManifestError {
#[error("manifest path does not exist: {0}")]
NotFound(PathBuf),
#[error("manifest io error: {0}")]
Io(String),
#[error("manifest parse failed: {0}")]
Parse(String),
#[error("manifest write failed: {0}")]
Write(String),
#[error("invalid manifest: {0}")]
Invalid(String),
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum MaterializerError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("variable referenced by manifest not found in registry: {0}")]
MissingVar(String),
#[error("materializer backend error: {0}")]
Backend(String),
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum RunnerError {
#[error("invalid env input: {0}")]
Invalid(String),
#[error("failed to spawn process: {0}")]
Spawn(String),
#[error("process exited with non-zero status: {0}")]
NonZeroExit(i32),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum ScannerError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("scanner pattern error: {0}")]
Pattern(String),
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum CoreError {
#[error(transparent)]
Metadata(#[from] MetadataError),
#[error(transparent)]
Secret(#[from] SecretError),
#[error(transparent)]
Manifest(#[from] ManifestError),
#[error(transparent)]
Materializer(#[from] MaterializerError),
#[error(transparent)]
Runner(#[from] RunnerError),
#[error(transparent)]
Scanner(#[from] ScannerError),
#[error(
"variable {id} has kind {expected:?} in metadata but the value is in the {found:?} tier"
)]
TierMismatch {
id: VarId,
expected: VarKind,
found: VarKind,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn duplicate_name_message_contains_name() {
let e = MetadataError::DuplicateName("API_KEY".to_owned());
assert!(e.to_string().contains("API_KEY"));
}
#[test]
fn core_error_from_metadata_via_question_mark() {
fn inner() -> Result<(), MetadataError> {
Err(MetadataError::Invalid("bad".into()))
}
fn outer() -> Result<(), CoreError> {
inner()?;
Ok(())
}
let err = outer().expect_err("expected error");
assert!(matches!(err, CoreError::Metadata(_)));
}
#[test]
fn io_error_converts_into_materializer_error() {
let io = std::io::Error::other("disk full");
let me: MaterializerError = io.into();
assert!(me.to_string().contains("disk full"));
}
}