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}
75
76impl fmt::Display for IncludeError {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 match self {
79 Self::Io(error) => write!(f, "filesystem error: {error}"),
80 Self::Parse { format, source } => write!(f, "{format} parse error: {source}"),
81 Self::UnknownFormat { path } => write!(
82 f,
83 "unsupported config format (expected .yml, .yaml, or .json): {}",
84 path.display()
85 ),
86 Self::ReadOnly { path } => {
87 write!(f, "config file is read-only: {}", path.display())
88 }
89 Self::DuplicateId { id } => write!(f, "duplicate entry id `{id}`"),
90 Self::EntryNotFound { id } => write!(f, "entry `{id}` not found"),
91 Self::InvalidId { id } => {
92 write!(
93 f,
94 "invalid entry id `{id}`: must be non-empty and contain no `:`"
95 )
96 }
97 Self::InvalidName => write!(f, "entry name must be non-empty"),
98 Self::MissingEnv { expression } => {
99 write!(f, "environment variable unset: `{expression}`")
100 }
101 Self::UnknownExpression { expression } => {
102 write!(f, "unsupported template expression `{expression}`")
103 }
104 Self::Unterminated { input } => {
105 write!(f, "unterminated template `${{{{`}} in `{input}`")
106 }
107 Self::NotInTree => write!(f, "entry does not belong to this tree"),
108 Self::Cycle => write!(f, "cannot move an entry into its own subtree"),
109 Self::Watch { source } => write!(f, "file watcher error: {source}"),
110 }
111 }
112}
113
114impl StdError for IncludeError {
115 fn source(&self) -> Option<&(dyn StdError + 'static)> {
116 match self {
117 Self::Io(error) => Some(error),
118 Self::Parse { source, .. } => Some(source.as_ref()),
119 Self::Watch { source } => Some(source.as_ref()),
120 _ => None,
121 }
122 }
123}
124
125impl From<io::Error> for IncludeError {
126 fn from(error: io::Error) -> Self {
127 Self::Io(error)
128 }
129}