1use std::error::Error as StdError;
4use std::fmt;
5use std::io;
6use std::path::PathBuf;
7
8pub type Result<T> = std::result::Result<T, IncludeError>;
10
11#[derive(Debug)]
13pub enum IncludeError {
14 Io(io::Error),
16 Parse {
18 format: &'static str,
20 source: Box<dyn StdError + Send + Sync>,
22 },
23 UnknownFormat {
25 path: PathBuf,
27 },
28 ReadOnly {
30 path: PathBuf,
32 },
33 DuplicateId {
35 id: String,
37 },
38 EntryNotFound {
40 id: String,
42 },
43 InvalidId {
45 id: String,
47 },
48 InvalidName,
50 MissingEnv {
52 expression: String,
54 },
55 UnknownExpression {
57 expression: String,
59 },
60 Unterminated {
62 input: String,
64 },
65 NotInTree,
67 Cycle,
69 Watch {
71 source: Box<dyn StdError + Send + Sync>,
73 },
74 Message {
77 message: String,
79 },
80}
81
82impl fmt::Display for IncludeError {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match self {
85 Self::Io(error) => write!(f, "filesystem error: {error}"),
86 Self::Parse { format, source } => write!(f, "{format} parse error: {source}"),
87 Self::UnknownFormat { path } => write!(
88 f,
89 "unsupported config format (expected .yml, .yaml, or .json): {}",
90 path.display()
91 ),
92 Self::ReadOnly { path } => {
93 write!(f, "config file is read-only: {}", path.display())
94 }
95 Self::DuplicateId { id } => write!(f, "duplicate entry id `{id}`"),
96 Self::EntryNotFound { id } => write!(f, "entry `{id}` not found"),
97 Self::InvalidId { id } => {
98 write!(
99 f,
100 "invalid entry id `{id}`: must be non-empty and contain no `:`"
101 )
102 }
103 Self::InvalidName => write!(f, "entry name must be non-empty"),
104 Self::MissingEnv { expression } => {
105 write!(f, "environment variable unset: `{expression}`")
106 }
107 Self::UnknownExpression { expression } => {
108 write!(f, "unsupported template expression `{expression}`")
109 }
110 Self::Unterminated { input } => {
111 write!(f, "unterminated template `${{{{`}} in `{input}`")
112 }
113 Self::NotInTree => write!(f, "entry does not belong to this tree"),
114 Self::Cycle => write!(f, "cannot move an entry into its own subtree"),
115 Self::Watch { source } => write!(f, "file watcher error: {source}"),
116 Self::Message { message } => write!(f, "{message}"),
117 }
118 }
119}
120
121impl StdError for IncludeError {
122 fn source(&self) -> Option<&(dyn StdError + 'static)> {
123 match self {
124 Self::Io(error) => Some(error),
125 Self::Parse { source, .. } => Some(source.as_ref()),
126 Self::Watch { source } => Some(source.as_ref()),
127 _ => None,
128 }
129 }
130}
131
132impl From<io::Error> for IncludeError {
133 fn from(error: io::Error) -> Self {
134 Self::Io(error)
135 }
136}