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}