Skip to main content

statime_config/
error.rs

1use std::{fmt, path::PathBuf};
2
3use crate::tree::{ConfigPath, Origin};
4
5/// Everything that can go wrong while loading a configuration.
6#[derive(Debug)]
7pub enum ConfigError {
8    /// Merging would overwrite an existing value at the given path.
9    ///
10    /// The origins are those of the settings involved; they are `None` when a
11    /// setting has not been attributed to a document.
12    OverwriteNotAllowed {
13        position: ConfigPath,
14        current_origin: Option<Origin>,
15        incoming_origin: Option<Origin>,
16    },
17
18    /// A value that is required, and that has no built-in default, was not
19    /// supplied by any document.
20    MissingRequiredValue { position: ConfigPath },
21
22    /// A configuration document could not be read.
23    CouldNotRead {
24        path: PathBuf,
25        cause: std::io::Error,
26    },
27
28    /// A configuration document is not valid TOML.
29    CouldNotParse {
30        path: PathBuf,
31        cause: toml::de::Error,
32    },
33
34    /// A system configuration fragment set `use-system-config`. Only the main
35    /// configuration may do so.
36    DirectiveNotAllowed { path: PathBuf },
37
38    /// Documents provide different variants for an enum during a merge.
39    MismatchedVariants {
40        position: ConfigPath,
41        current: &'static str,
42        incoming: &'static str,
43    },
44}
45
46impl fmt::Display for ConfigError {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        match self {
49            Self::OverwriteNotAllowed {
50                position,
51                current_origin,
52                incoming_origin,
53            } => write!(
54                f,
55                "`{position}` is set by both {} and {}",
56                Described(current_origin),
57                Described(incoming_origin),
58            ),
59            Self::MissingRequiredValue { position } => {
60                write!(f, "`{position}` is required, but was never set")
61            }
62            Self::CouldNotRead { path, cause } => {
63                write!(f, "could not read `{}`: {cause}", path.display())
64            }
65            Self::CouldNotParse { path, cause } => {
66                write!(f, "could not parse `{}`: {cause}", path.display())
67            }
68            Self::DirectiveNotAllowed { path } => write!(
69                f,
70                "`{}` sets `use-system-config`, which only the main configuration may do",
71                path.display()
72            ),
73            Self::MismatchedVariants {
74                position,
75                current,
76                incoming,
77            } => write!(
78                f,
79                "At {position}, trying to merge {current} with differen variant {incoming}, which is not possible",
80            ),
81        }
82    }
83}
84
85impl std::error::Error for ConfigError {
86    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
87        match self {
88            Self::CouldNotRead { cause, .. } => Some(cause),
89            Self::CouldNotParse { cause, .. } => Some(cause),
90            _ => None,
91        }
92    }
93}
94
95/// Names the document a value came from, for a setting that may never have been
96/// attributed to one.
97struct Described<'a>(&'a Option<Origin>);
98
99impl fmt::Display for Described<'_> {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        match self.0 {
102            Some(Origin::MainConfig(path)) | Some(Origin::SystemConfig(path)) => {
103                write!(f, "`{}`", path.display())
104            }
105            Some(Origin::BuiltInDefault) => write!(f, "the built-in defaults"),
106            None => write!(f, "an unknown document"),
107        }
108    }
109}