Skip to main content

module_util/file/
error.rs

1//! [`File`] error.
2//!
3//! [`File`]: super::File
4
5use std::fmt;
6use std::path::PathBuf;
7use std::string::ToString;
8
9use crate::evaluator::Trace;
10use crate::evaluator::dfs;
11
12///////////////////////////////////////////////////////////////////////////////
13
14/// Error for the [`File`] evaluator.
15///
16/// # Example
17///
18/// ```rust
19/// # use module_util::file::error::Error;
20/// use module_util::evaluator::dfs;
21///
22/// let mut err = Error::new(dfs::Error::Merge({
23///     let mut e = module::merge::Error::collision();
24///     e.field.push_back("my");
25///     e.field.push_back("super");
26///     e.field.push_back("awesome");
27///     e.field.push_back("field");
28///     e
29/// }));
30///
31/// err.trace.push("module 1".into());
32/// err.trace.push("module 2".into());
33/// err.trace.push("module 3".into());
34///
35/// assert_eq!(format!("{err}"),
36/// r#"value collision while evaluating "my.super.awesome.field"
37///
38///     in module 3
39///   from module 2
40///   from module 1
41/// "#);
42///
43/// assert_eq!(format!("{err}"), format!("{err:#}"));
44/// ```
45///
46/// ```rust
47/// # use module_util::file::error::Error;
48/// use module_util::evaluator::dfs;
49///
50/// let mut err = Error::new(dfs::Error::Merge(module::merge::Error::collision()));
51///
52/// err.trace.push("module 1".into());
53///
54/// assert_eq!(format!("{err}"),
55/// r#"value collision
56///
57///     in module 1
58/// "#);
59///
60/// assert_eq!(format!("{err}"), format!("{err:#}"));
61/// ```
62///
63/// ```rust
64/// # use module_util::file::error::Error;
65/// use module_util::evaluator::dfs;
66/// use std::io;
67///
68/// let mut err = Error::new({
69///     let mut e = module::merge::Error::other(io::Error::other("invalid data"));
70///     e.field.push_back("user");
71///     e.field.push_back("email");
72///     dfs::Error::Merge(e)
73/// });
74///
75/// err.trace.push("module 1".into());
76/// err.trace.push("module 2".into());
77///
78/// assert_eq!(format!("{err}"),
79/// r#"invalid data while evaluating "user.email"
80///
81///     in module 2
82///   from module 1
83/// "#);
84///
85/// assert_eq!(format!("{err}"), format!("{err:#}"));
86/// ```
87///
88/// ```rust
89/// # use module_util::file::error::Error;
90/// use module_util::evaluator::{dfs, Trace};
91/// use std::io;
92///
93/// let err = Error::new(dfs::Error::other(io::Error::other("some other error")))
94///     .with_trace({
95///         let mut t = Trace::empty();
96///         t.push("module 1".into());
97///         t.push("module 2".into());
98///         t.push("module 3".into());
99///         t
100///     });
101///
102/// assert_eq!(format!("{err}"),
103/// r#"some other error
104///
105///     in module 3
106///   from module 2
107///   from module 1
108/// "#);
109///
110/// assert_eq!(format!("{err}"), format!("{err:#}"));
111/// ```
112///
113/// [`File`]: super::File
114#[expect(clippy::manual_non_exhaustive)]
115pub struct Error {
116    _priv: (),
117
118    /// Evaluation error.
119    pub error: dfs::Error,
120
121    /// Module trace.
122    pub trace: Trace<PathBuf>,
123}
124
125impl fmt::Debug for Error {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.debug_struct("Error")
128            .field("error", &self.error)
129            .field("trace", &self.trace)
130            .finish()
131    }
132}
133
134impl fmt::Display for Error {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        match self.error {
137            dfs::Error::Merge(ref e) => {
138                e.message(f)?;
139
140                if !e.field.is_empty() {
141                    write!(f, " while evaluating {:?}", e.field)?;
142                }
143
144                writeln!(f)?;
145            }
146
147            dfs::Error::Other(ref e) => {
148                writeln!(f, "{}", e.to_string().trim_end())?;
149            }
150        }
151
152        let mut trace = self.trace.iter().rev();
153        if let Some(first) = trace.next() {
154            writeln!(f)?;
155            writeln!(f, "    in {}", first.display())?;
156            for path in trace {
157                writeln!(f, "  from {}", path.display())?;
158            }
159        }
160
161        Ok(())
162    }
163}
164
165impl std::error::Error for Error {}
166
167impl Error {
168    /// Create a new [`Error`].
169    #[must_use]
170    pub const fn new(error: dfs::Error) -> Self {
171        Self {
172            _priv: (),
173            error,
174            trace: Trace::empty(),
175        }
176    }
177
178    /// Set the `trace` of the error.
179    #[must_use]
180    pub fn with_trace(mut self, trace: Trace<PathBuf>) -> Self {
181        self.trace = trace;
182        self
183    }
184}