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 JsExpression {
63 expression: String,
65 message: String,
68 },
69 Unterminated {
71 input: String,
73 },
74 NotInTree,
76 Cycle,
78 Watch {
80 source: Box<dyn StdError + Send + Sync>,
82 },
83 Message {
86 message: String,
88 },
89}
90
91impl fmt::Display for IncludeError {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 Self::Io(error) => write!(f, "filesystem error: {error}"),
95 Self::Parse { format, source } => write!(f, "{format} parse error: {source}"),
96 Self::UnknownFormat { path } => write!(
97 f,
98 "unsupported config format (expected .yml, .yaml, or .json): {}",
99 path.display()
100 ),
101 Self::ReadOnly { path } => {
102 write!(f, "config file is read-only: {}", path.display())
103 }
104 Self::DuplicateId { id } => write!(f, "duplicate entry id `{id}`"),
105 Self::EntryNotFound { id } => write!(f, "entry `{id}` not found"),
106 Self::InvalidId { id } => {
107 write!(
108 f,
109 "invalid entry id `{id}`: must be non-empty and contain no `:`"
110 )
111 }
112 Self::InvalidName => write!(f, "entry name must be non-empty"),
113 Self::MissingEnv { expression } => {
114 write!(f, "environment variable unset: `{expression}`")
115 }
116 Self::UnknownExpression { expression } => {
117 write!(f, "unsupported template expression `{expression}`")
118 }
119 Self::JsExpression {
120 expression,
121 message,
122 } => write!(f, "!!js expression `{expression}` failed: {message}"),
123 Self::Unterminated { input } => {
124 write!(f, "unterminated template `${{{{`}} in `{input}`")
125 }
126 Self::NotInTree => write!(f, "entry does not belong to this tree"),
127 Self::Cycle => write!(f, "cannot move an entry into its own subtree"),
128 Self::Watch { source } => write!(f, "file watcher error: {source}"),
129 Self::Message { message } => write!(f, "{message}"),
130 }
131 }
132}
133
134impl StdError for IncludeError {
135 fn source(&self) -> Option<&(dyn StdError + 'static)> {
136 match self {
137 Self::Io(error) => Some(error),
138 Self::Parse { source, .. } => Some(source.as_ref()),
139 Self::Watch { source } => Some(source.as_ref()),
140 _ => None,
141 }
142 }
143}
144
145impl From<io::Error> for IncludeError {
146 fn from(error: io::Error) -> Self {
147 Self::Io(error)
148 }
149}