autumn_plugin_topcoat/
error.rs1use std::fmt;
4
5#[non_exhaustive]
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8pub enum PathError {
9 #[error("path is empty")]
11 Empty,
12 #[error("`/` matches every path and cannot be a prefix")]
14 Root,
15 #[error("path {path:?} must start with '/'")]
17 #[non_exhaustive]
18 MissingLeadingSlash {
19 path: String,
21 },
22 #[error("path {path:?} must not end with '/'")]
24 #[non_exhaustive]
25 TrailingSlash {
26 path: String,
28 },
29 #[error("path {path:?} has an empty segment")]
31 #[non_exhaustive]
32 EmptySegment {
33 path: String,
35 },
36 #[error("path {path:?} has a '.' or '..' segment")]
38 #[non_exhaustive]
39 DotSegment {
40 path: String,
42 },
43 #[error("path {path:?} contains forbidden character {ch:?}")]
45 #[non_exhaustive]
46 ForbiddenChar {
47 path: String,
49 ch: char,
51 },
52 #[error("path {path:?} belongs to {owner}")]
54 #[non_exhaustive]
55 Reserved {
56 path: String,
58 owner: &'static str,
60 },
61}
62
63#[non_exhaustive]
65#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
66pub enum ConfigError {
67 #[error(
69 "the plugin has no Topcoat router; call TopcoatPlugin::router or TopcoatPlugin::router_with"
70 )]
71 NoRouter,
72 #[error("the Topcoat router has no routes")]
74 EmptyRouter,
75 #[error("invalid mount path {input:?}: {cause}")]
77 #[non_exhaustive]
78 InvalidMount {
79 input: String,
81 cause: PathError,
83 },
84 #[error("invalid excluded prefix {input:?}: {cause}")]
86 #[non_exhaustive]
87 InvalidExclude {
88 input: String,
90 cause: PathError,
92 },
93 #[error("invalid runtime prefix {input:?}: {cause}")]
95 #[non_exhaustive]
96 InvalidRuntimePrefix {
97 input: String,
99 cause: PathError,
101 },
102 #[error("excluded prefix {prefix} must be strictly inside the mount {mount}")]
104 #[non_exhaustive]
105 ExcludedOutsideMount {
106 prefix: String,
108 mount: String,
110 },
111 #[error("excluded prefix {prefix} overlaps the Topcoat namespace /_topcoat")]
113 #[non_exhaustive]
114 ExcludedOverlapsInternal {
115 prefix: String,
117 },
118 #[error("runtime prefix {prefix} must be inside the mount {mount} or under /_topcoat")]
120 #[non_exhaustive]
121 RuntimePrefixOutsideMount {
122 prefix: String,
124 mount: String,
126 },
127 #[error("runtime prefix {prefix} is under the excluded prefix {excluded}")]
129 #[non_exhaustive]
130 RuntimePrefixExcluded {
131 prefix: String,
133 excluded: String,
135 },
136}
137
138#[derive(Debug, Clone, PartialEq, Eq)]
143pub struct ConfigErrors(pub(crate) Vec<ConfigError>);
144
145impl ConfigErrors {
146 #[must_use]
148 pub const fn as_slice(&self) -> &[ConfigError] {
149 self.0.as_slice()
150 }
151
152 pub fn iter(&self) -> std::slice::Iter<'_, ConfigError> {
154 self.0.iter()
155 }
156
157 #[must_use]
159 pub const fn len(&self) -> usize {
160 self.0.len()
161 }
162
163 #[must_use]
165 pub const fn is_empty(&self) -> bool {
166 self.0.is_empty()
167 }
168}
169
170impl IntoIterator for ConfigErrors {
171 type Item = ConfigError;
172 type IntoIter = std::vec::IntoIter<ConfigError>;
173
174 fn into_iter(self) -> Self::IntoIter {
175 self.0.into_iter()
176 }
177}
178
179impl<'a> IntoIterator for &'a ConfigErrors {
180 type Item = &'a ConfigError;
181 type IntoIter = std::slice::Iter<'a, ConfigError>;
182
183 fn into_iter(self) -> Self::IntoIter {
184 self.0.iter()
185 }
186}
187
188impl fmt::Display for ConfigErrors {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 for (index, error) in self.0.iter().enumerate() {
191 if index > 0 {
192 f.write_str("; ")?;
193 }
194 write!(f, "{error}")?;
195 }
196 Ok(())
197 }
198}
199
200impl std::error::Error for ConfigErrors {}
201
202#[non_exhaustive]
204#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
205pub enum StartupError {
206 #[error("invalid TopcoatPlugin configuration: {0}")]
208 Config(ConfigErrors),
209 #[error("the Topcoat router factory failed: {message}")]
211 #[non_exhaustive]
212 Factory {
213 message: String,
215 },
216 #[error("the plugin cannot build the Topcoat router: {message}")]
219 #[non_exhaustive]
220 RouterBuild {
221 message: String,
223 },
224 #[error("CspDenied: the Content-Security-Policy blocks Topcoat ({findings}); {advice}")]
226 #[non_exhaustive]
227 CspDenied {
228 findings: String,
230 advice: String,
232 },
233}
234
235#[non_exhaustive]
237#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
238pub enum AppDataError {
239 #[error("autumn-plugin-topcoat does not serve this render")]
241 NotMounted,
242 #[error("AppState has no extension of type {type_name}")]
244 #[non_exhaustive]
245 MissingExtension {
246 type_name: &'static str,
248 },
249}
250
251#[non_exhaustive]
253#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
254pub enum RequestScopeError {
255 #[error("no Autumn HTTP request backs this render")]
257 Detached,
258 #[error("{what} is not available on this request")]
260 #[non_exhaustive]
261 Unavailable {
262 what: &'static str,
264 },
265}