use std::path::PathBuf;
use crate::change::ChangeId;
use crate::config::ConfigError;
use crate::follows::path::AttrPathParseError;
use crate::validate::ValidationError;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error(transparent)]
Flake(#[from] crate::Error),
#[error(transparent)]
Config(#[from] ConfigError),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("could not open flake.nix at {path}", path = path.display())]
FlakeNotFound {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("no flake.nix in directory {path}", path = path.display())]
FlakeDirEmpty { path: PathBuf },
#[error("`--flake` and `--lock` cannot be used with `follow [PATHS]`")]
IncompatibleFollowOptions,
#[error("no URI provided")]
NoUri,
#[error("no input id provided")]
NoId,
#[error("no inputs found in the flake")]
NoInputs,
#[error("invalid URI '{uri}'")]
InvalidUri {
uri: String,
#[source]
source: nix_uri::NixUriError,
},
#[error("invalid input id '{id}'")]
InvalidInputId {
id: String,
#[source]
source: AttrPathParseError,
},
#[error("invalid follows path '{path}'")]
InvalidFollowsPath {
path: String,
#[source]
source: AttrPathParseError,
},
#[error("could not infer id from flake reference '{uri}'")]
CouldNotInferId { uri: String },
#[error("input '{id}' has no pinnable URL (it may use follows or a non-standard format)")]
InputNotPinnable { id: String },
#[error("could not remove input '{id}'")]
CouldNotRemove { id: ChangeId },
#[error("could not read lock file '{path}'", path = path.display())]
LockFile {
path: PathBuf,
#[source]
source: crate::Error,
},
#[error("could not create follows relationship for '{id}'")]
FollowsCreateFailed { id: String },
#[error("validation failed after applying edits ({} issue(s))", .0.len())]
ValidationAfterEdit(Vec<ValidationError>),
#[error("{} file(s) failed during batch processing", failures.len())]
Batch {
failures: Vec<(PathBuf, Box<Error>)>,
},
}
impl Error {
pub fn batch_bullets(&self) -> Option<Vec<String>> {
match self {
Self::Batch { failures } => Some(
failures
.iter()
.map(|(path, err)| {
format!(
"{}: {}",
path.display(),
chain_layers(err.as_ref()).join(": ")
)
})
.collect(),
),
_ => None,
}
}
pub fn validation_bullets(&self) -> Option<Vec<String>> {
match self {
Self::ValidationAfterEdit(errs) => Some(errs.iter().map(|e| e.to_string()).collect()),
_ => None,
}
}
}
pub fn chain_layers(err: &(dyn std::error::Error + 'static)) -> Vec<String> {
let mut layers = vec![err.to_string()];
let mut current = err.source();
while let Some(source) = current {
layers.push(source.to_string());
current = source.source();
}
layers
}
pub type Result<T> = std::result::Result<T, Error>;