Skip to main content

par_term_config/
error.rs

1//! Typed error variants for the par-term-config crate.
2//!
3//! Provides structured error types for config I/O and validation operations.
4//! These are used internally and exposed for library consumers who want to
5//! match on specific failure modes instead of opaque `anyhow` strings.
6
7use std::fmt;
8
9/// Errors that can occur when loading or saving configuration.
10///
11/// These errors are produced internally by `Config::load` and
12/// `Config::save`, as well as by any helper that reads or writes YAML
13/// state files.
14///
15/// For backward compatibility with existing callers that use `anyhow`, both
16/// functions still return `anyhow::Result`; `ConfigError` values are
17/// automatically coerced via the `From` impl that `anyhow` provides for any
18/// `std::error::Error`.
19///
20/// # Example
21///
22/// ```rust,no_run
23/// use par_term_config::ConfigError;
24///
25/// fn check_load_err(e: &anyhow::Error) {
26///     if let Some(cfg_err) = e.downcast_ref::<ConfigError>() {
27///         match cfg_err {
28///             ConfigError::Io(io) => eprintln!("I/O error: {io}"),
29///             ConfigError::Parse(p) => eprintln!("YAML parse error: {p}"),
30///             ConfigError::Validation(msg) => eprintln!("Validation: {msg}"),
31///             ConfigError::PathTraversal(msg) => eprintln!("Path traversal: {msg}"),
32///         }
33///     }
34/// }
35/// ```
36#[derive(Debug)]
37pub enum ConfigError {
38    /// An I/O error occurred reading or writing the config file.
39    Io(std::io::Error),
40
41    /// The config file contained invalid YAML that could not be parsed.
42    Parse(serde_yaml_ng::Error),
43
44    /// A field value failed semantic validation.
45    ///
46    /// The inner string describes which field is invalid and why.
47    Validation(String),
48
49    /// A path resolved outside the expected configuration directory,
50    /// indicating a potential directory traversal attempt.
51    ///
52    /// The inner string includes the offending path and the expected base.
53    PathTraversal(String),
54}
55
56impl fmt::Display for ConfigError {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match self {
59            ConfigError::Io(e) => write!(f, "I/O error reading config: {e}"),
60            ConfigError::Parse(e) => write!(f, "YAML parse error in config: {e}"),
61            ConfigError::Validation(msg) => write!(f, "Config validation error: {msg}"),
62            ConfigError::PathTraversal(msg) => write!(f, "Path traversal detected: {msg}"),
63        }
64    }
65}
66
67impl std::error::Error for ConfigError {
68    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
69        match self {
70            ConfigError::Io(e) => Some(e),
71            ConfigError::Parse(e) => Some(e),
72            ConfigError::Validation(_) | ConfigError::PathTraversal(_) => None,
73        }
74    }
75}
76
77impl From<std::io::Error> for ConfigError {
78    fn from(e: std::io::Error) -> Self {
79        ConfigError::Io(e)
80    }
81}
82
83impl From<serde_yaml_ng::Error> for ConfigError {
84    fn from(e: serde_yaml_ng::Error) -> Self {
85        ConfigError::Parse(e)
86    }
87}