1use std::fmt;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum SignUpFault {
11 AuthDisabled,
15 ReservedField(String),
19 FileField(String),
23 RelationshipField(String),
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
32#[non_exhaustive]
33pub enum ProfileFault {
34 AuthDisabled,
38 ReservedField(String),
42 FileField(String),
46 RelationshipField(String),
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
55#[non_exhaustive]
56pub enum ResetFault {
57 AuthDisabled,
61}
62
63impl fmt::Display for ResetFault {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 Self::AuthDisabled => f.write_str(
67 "a password reset needs auth: `Auth::disabled()` serves no sign-in to reset",
68 ),
69 }
70 }
71}
72
73impl fmt::Display for ProfileFault {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 Self::AuthDisabled => {
77 f.write_str("a profile page needs auth: `Auth::disabled()` signs nobody in to edit")
78 }
79 Self::ReservedField(field) => write!(
80 f,
81 "the profile form declares a field named '{field}', which its POST carries \
82 itself: rename the field"
83 ),
84 Self::FileField(field) => write!(
85 f,
86 "the profile form declares a file field '{field}', which its POST does not \
87 upload: edit the file on a resource's form"
88 ),
89 Self::RelationshipField(field) => write!(
90 f,
91 "the profile form declares a relationship choice '{field}', whose options no \
92 route serves the profile page: offer static options"
93 ),
94 }
95 }
96}
97
98impl fmt::Display for SignUpFault {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 Self::AuthDisabled => f.write_str(
102 "registration needs auth: `Auth::disabled()` serves no sign-in for a new \
103 account to land on",
104 ),
105 Self::ReservedField(field) => write!(
106 f,
107 "the sign-up form declares a field named '{field}', which its POST carries \
108 itself: rename the field"
109 ),
110 Self::FileField(field) => write!(
111 f,
112 "the sign-up form declares a file field '{field}', which its POST does not \
113 upload: ask for the file once the account signs in"
114 ),
115 Self::RelationshipField(field) => write!(
116 f,
117 "the sign-up form declares a relationship choice '{field}', which would list \
118 records to a visitor nobody signed in: offer static options"
119 ),
120 }
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
127#[non_exhaustive]
128pub enum AuthMailFault {
129 Unset {
134 feature: &'static str,
136 },
137 BaseUrl(String),
141 MissingMail,
145}
146
147impl fmt::Display for AuthMailFault {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 match self {
150 Self::Unset { feature } => write!(
151 f,
152 "`Auth::{feature}` sends mail, but the auth sets no sender: install one with \
153 `Auth::mail(AuthMail::new(..))`"
154 ),
155 Self::BaseUrl(url) => write!(
156 f,
157 "the auth mail's base URL '{url}' is not an absolute http or https URL: pass the \
158 scheme and host the app answers on, such as `https://admin.example.com`"
159 ),
160 Self::MissingMail => f.write_str(
161 "the panel's auth mails links, but the router registers no `Mailer`: add \
162 one with `.with(Mailer::new(..))` before mounting the panel",
163 ),
164 }
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
171#[non_exhaustive]
172pub enum VerificationFault {
173 AuthDisabled,
177}
178
179impl fmt::Display for VerificationFault {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 match self {
182 Self::AuthDisabled => f.write_str(
183 "email verification needs auth: `Auth::disabled()` signs nobody in to verify",
184 ),
185 }
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
192#[non_exhaustive]
193pub enum TenantRegistrationFault {
194 AuthDisabled,
198 ReservedField(String),
202 FileField(String),
206 RelationshipField(String),
210}
211
212impl fmt::Display for TenantRegistrationFault {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 match self {
215 Self::AuthDisabled => f.write_str(
216 "tenant registration needs auth: `Auth::disabled()` signs nobody in to register \
217 a tenant",
218 ),
219 Self::ReservedField(field) => write!(
220 f,
221 "the tenant registration form declares a field named '{field}', which its POST \
222 carries itself: rename the field"
223 ),
224 Self::FileField(field) => write!(
225 f,
226 "the tenant registration form declares a file field '{field}', which its POST \
227 does not upload: ask for the file on a resource's form"
228 ),
229 Self::RelationshipField(field) => write!(
230 f,
231 "the tenant registration form declares a relationship choice '{field}', whose \
232 options no route serves the registration page: offer static options"
233 ),
234 }
235 }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
241#[non_exhaustive]
242pub enum TenantProfileFault {
243 AuthDisabled,
247 ReservedField(String),
251 FileField(String),
255 RelationshipField(String),
259}
260
261impl fmt::Display for TenantProfileFault {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 match self {
264 Self::AuthDisabled => f.write_str(
265 "a tenant settings page needs auth: `Auth::disabled()` signs nobody in to edit a \
266 tenant",
267 ),
268 Self::ReservedField(field) => write!(
269 f,
270 "the tenant settings form declares a field named '{field}', which its POST \
271 carries itself: rename the field"
272 ),
273 Self::FileField(field) => write!(
274 f,
275 "the tenant settings form declares a file field '{field}', which its POST does \
276 not upload: edit the file on a resource's form"
277 ),
278 Self::RelationshipField(field) => write!(
279 f,
280 "the tenant settings form declares a relationship choice '{field}', whose \
281 options no route serves the settings page: offer static options"
282 ),
283 }
284 }
285}
286
287#[derive(Debug, Clone, PartialEq, Eq)]
290#[non_exhaustive]
291pub enum TwoFactorFault {
292 AuthDisabled,
296 MissingKey,
301}
302
303impl fmt::Display for TwoFactorFault {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 match self {
306 Self::AuthDisabled => f.write_str(
307 "two-factor sign-in needs auth: `Auth::disabled()` signs nobody in to ask a \
308 second factor of",
309 ),
310 Self::MissingKey => f.write_str(
311 "two-factor sign-in seals each authenticator secret under the router's cookie \
312 key, and the router has none: register a `topcoat::cookie::Key` with `.with(..)`",
313 ),
314 }
315 }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq)]
321#[non_exhaustive]
322pub enum OidcFault {
323 AuthDisabled,
327 ExternalAuth,
331 MissingKey,
336 BaseUrl(String),
340 NoProvider,
344 ProviderKey(String),
348 DuplicateProvider(String),
352 Issuer(String),
356 ReservedParam(String, String),
361}
362
363impl fmt::Display for OidcFault {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 match self {
366 Self::AuthDisabled => f.write_str(
367 "provider sign-in needs auth: `Auth::disabled()` signs nobody in to link an \
368 identity to",
369 ),
370 Self::ExternalAuth => f.write_str(
371 "provider sign-in cannot sit beside `Auth::external`: the app signs every user in \
372 itself, so sign in through the provider there",
373 ),
374 Self::MissingKey => f.write_str(
375 "provider sign-in seals each pending sign-in under the router's cookie key, and \
376 the router has none: register a `topcoat::cookie::Key` with `.with(..)`",
377 ),
378 Self::BaseUrl(url) => write!(
379 f,
380 "provider sign-in's base URL '{url}' is not an absolute `https` origin: pass the \
381 app's public origin, such as `https://app.example.com`"
382 ),
383 Self::NoProvider => {
384 f.write_str("provider sign-in has no provider: add one with `Oidc::provider`")
385 }
386 Self::ProviderKey(key) => write!(
387 f,
388 "provider key '{key}' cannot name a route: use lowercase ASCII letters, digits \
389 and `-`"
390 ),
391 Self::DuplicateProvider(key) => write!(
392 f,
393 "two providers have the key '{key}': give each a distinct key"
394 ),
395 Self::Issuer(key) => write!(
396 f,
397 "provider '{key}' has an issuer that is not an `https` URL, nor an `http` one on \
398 a loopback host"
399 ),
400 Self::ReservedParam(key, name) => write!(
401 f,
402 "provider '{key}' sets the authorization parameter '{name}', which the panel \
403 sets itself: drop it"
404 ),
405 }
406 }
407}