use std::path::PathBuf;
use thiserror::Error;
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum DodotError {
#[error("filesystem error at {path}: {source}")]
Fs {
path: PathBuf,
source: std::io::Error,
},
#[error("symlink conflict: {path} already exists and is not managed by dodot")]
SymlinkConflict { path: PathBuf },
#[error("protected path: {path} cannot be symlinked")]
ProtectedPath { path: PathBuf },
#[error("pack not found: {name}")]
PackNotFound { name: String },
#[error("pack is invalid: {name}: {reason}")]
PackInvalid { name: String, reason: String },
#[error("pack ordering collision: display name `{display_name}` resolves to multiple packs:\n{}", .paths.iter().map(|p| format!(" - {}", p.display())).collect::<Vec<_>>().join("\n"))]
PackOrderingCollision {
display_name: String,
paths: Vec<PathBuf>,
},
#[error("handler not found: {name}")]
HandlerNotFound { name: String },
#[error("config error: {0}")]
Config(String),
#[error("command failed: {command} (exit code {exit_code})\n{stderr}")]
CommandFailed {
command: String,
exit_code: i32,
stderr: String,
},
#[error("invalid pattern {pattern}: {reason}")]
InvalidPattern { pattern: String, reason: String },
#[error("cross-pack deployment conflict detected (--force does not override this):\n{}", crate::conflicts::format_conflicts(.conflicts))]
CrossPackConflict {
conflicts: Vec<crate::conflicts::Conflict>,
},
#[error(
"routing override conflict in pack `{pack}` for `{rel_path}`:\n \
filename routes via its prefix, and `[symlink.targets]` declares `{config_target}`.\n \
pick one — either rename the file (drop the `home.`/`app.`/`xdg.`/`lib.` or `_home/`/`_xdg/`/`_app/`/`_lib/` prefix) \
or remove the `[symlink.targets]` entry."
)]
RoutingOverrideConflict {
pack: String,
rel_path: String,
config_target: String,
},
#[error("preprocessing failed for {source_file} ({preprocessor}): {message}")]
PreprocessorError {
preprocessor: String,
source_file: PathBuf,
message: String,
},
#[error("preprocessing collision in pack \"{pack}\": {source_file} expands to {expanded_name}, which conflicts with an existing pack file or another preprocessor's output")]
PreprocessorCollision {
pack: String,
source_file: String,
expanded_name: String,
},
#[error("template render failed for {}:\n {message}", source_file.display())]
TemplateRender {
source_file: PathBuf,
message: String,
},
#[error("template variable name \"{name}\" is reserved (dodot and env are built-in namespaces); choose a different name in [preprocessor.template.vars]")]
TemplateReservedVar { name: String },
#[error("unresolved dodot-conflict markers in {} at line{} {}\n resolve the conflict block(s) with `git diff -- '{}'` and remove the dodot-conflict marker lines, then re-run.", source_file.display(), if line_numbers.len() == 1 { "" } else { "s" }, line_numbers.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", "), source_file.display())]
UnresolvedConflictMarker {
source_file: PathBuf,
line_numbers: Vec<usize>,
},
#[error("{0}")]
Other(String),
}
pub type Result<T> = std::result::Result<T, DodotError>;
pub(crate) fn fs_err(path: impl Into<PathBuf>, source: std::io::Error) -> DodotError {
DodotError::Fs {
path: path.into(),
source,
}
}