Skip to content

Commit af68f5e

Browse files
committed
Major changes
- Add Error trait - Use Option<Box<dyn Error>> - Update Responder to return a result - Update all core implementations
1 parent fb796fc commit af68f5e

18 files changed

+345
-209
lines changed

core/lib/src/catcher/types.rs

+117-48
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,94 @@
1-
use transient::{Any, CanRecoverFrom, Co, Downcast};
1+
use either::Either;
2+
use transient::{Any, CanRecoverFrom, Inv, Downcast, Transience};
3+
use crate::{http::Status, Request, Response};
24
#[doc(inline)]
35
pub use transient::{Static, Transient, TypeId};
46

5-
pub type ErasedError<'r> = Box<dyn Any<Co<'r>> + Send + Sync + 'r>;
6-
pub type ErasedErrorRef<'r> = dyn Any<Co<'r>> + Send + Sync + 'r;
7+
/// Polyfill for trait upcasting to [`Any`]
8+
pub trait AsAny<Tr: Transience>: Any<Tr> + Sealed {
9+
/// The actual upcast
10+
fn as_any(&self) -> &dyn Any<Tr>;
11+
/// convience typeid of the inner typeid
12+
fn trait_obj_typeid(&self) -> TypeId;
13+
}
14+
15+
use sealed::Sealed;
16+
mod sealed {
17+
use transient::{Any, Inv, TypeId};
18+
19+
use super::AsAny;
20+
21+
pub trait Sealed {}
22+
impl<'r, T: Any<Inv<'r>>> Sealed for T { }
23+
impl<'r, T: Any<Inv<'r>>> AsAny<Inv<'r>> for T {
24+
fn as_any(&self) -> &dyn Any<Inv<'r>> {
25+
self
26+
}
27+
fn trait_obj_typeid(&self) -> transient::TypeId {
28+
TypeId::of::<T>()
29+
}
30+
}
31+
}
32+
33+
/// This is the core of typed catchers. If an error type (returned by
34+
/// FromParam, FromRequest, FromForm, FromData, or Responder) implements
35+
/// this trait, it can be caught by a typed catcher. (TODO) This trait
36+
/// can be derived.
37+
pub trait Error<'r>: AsAny<Inv<'r>> + Send + Sync + 'r {
38+
/// Generates a default response for this type (or forwards to a default catcher)
39+
#[allow(unused_variables)]
40+
fn respond_to(&self, request: &'r Request<'_>) -> Result<Response<'r>, Status> {
41+
Err(Status::InternalServerError)
42+
}
43+
44+
/// A descriptive name of this error type. Defaults to the type name.
45+
fn name(&self) -> &'static str { std::any::type_name::<Self>() }
46+
47+
/// The error that caused this error. Defaults to None.
48+
///
49+
/// # Warning
50+
/// A typed catcher will not attempt to follow the source of an error
51+
/// more than once.
52+
fn source(&self) -> Option<&dyn Error<'r>> { None }
53+
54+
/// Status code
55+
fn status(&self) -> Status { Status::InternalServerError }
56+
}
757

8-
pub fn default_error_type<'r>() -> ErasedError<'r> {
9-
Box::new(())
58+
impl<'r> Error<'r> for std::convert::Infallible { }
59+
impl<'r, L: Error<'r>, R: Error<'r>> Error<'r> for Either<L, R> {
60+
fn respond_to(&self, request: &'r Request<'_>) -> Result<Response<'r>, Status> {
61+
match self {
62+
Self::Left(v) => v.respond_to(request),
63+
Self::Right(v) => v.respond_to(request),
64+
}
65+
}
66+
67+
fn name(&self) -> &'static str { std::any::type_name::<Self>() }
68+
69+
fn source(&self) -> Option<&dyn Error<'r>> {
70+
match self {
71+
Self::Left(v) => v.source(),
72+
Self::Right(v) => v.source(),
73+
}
74+
}
75+
76+
fn status(&self) -> Status {
77+
match self {
78+
Self::Left(v) => v.status(),
79+
Self::Right(v) => v.status(),
80+
}
81+
}
1082
}
1183

12-
pub fn downcast<'a, 'r, T: Transient + 'r>(v: &'a ErasedErrorRef<'r>) -> Option<&'a T>
13-
where T::Transience: CanRecoverFrom<Co<'r>>
84+
pub fn downcast<'a, 'r, T: Transient + 'r>(v: &'a dyn Error<'r>) -> Option<&'a T>
85+
where T::Transience: CanRecoverFrom<Inv<'r>>
1486
{
15-
v.downcast_ref()
87+
v.as_any().downcast_ref()
1688
}
1789

18-
/// Upcasts a value to `ErasedError`, falling back to a default if it doesn't implement
19-
/// `Transient`
90+
/// Upcasts a value to `Box<dyn Error<'r>>`, falling back to a default if it doesn't implement
91+
/// `Error`
2092
#[doc(hidden)]
2193
#[macro_export]
2294
macro_rules! resolve_typed_catcher {
@@ -26,9 +98,6 @@ macro_rules! resolve_typed_catcher {
2698

2799
Resolve::new($T).cast()
28100
});
29-
() => ({
30-
$crate::catcher::default_error_type()
31-
});
32101
}
33102

34103
pub use resolve_typed_catcher;
@@ -61,56 +130,56 @@ pub mod resolution {
61130
pub trait DefaultTypeErase<'r>: Sized {
62131
const SPECIALIZED: bool = false;
63132

64-
fn cast(self) -> ErasedError<'r> { Box::new(()) }
133+
fn cast(self) -> Option<Box<dyn Error<'r>>> { None }
65134
}
66135

67136
impl<'r, T: 'r> DefaultTypeErase<'r> for Resolve<'r, T> {}
68137

69138
/// "Specialized" "implementation" of `Transient` for `T: Transient`. This is
70139
/// what Rust will resolve `Resolve<T>::item` to when `T: Transient`.
71-
impl<'r, T: Transient + Send + Sync + 'r> Resolve<'r, T>
72-
where T::Transience: CanTranscendTo<Co<'r>>
140+
impl<'r, T: Error<'r> + Transient> Resolve<'r, T>
141+
where T::Transience: CanTranscendTo<Inv<'r>>
73142
{
74143
pub const SPECIALIZED: bool = true;
75144

76-
pub fn cast(self) -> ErasedError<'r> { Box::new(self.0) }
145+
pub fn cast(self) -> Option<Box<dyn Error<'r>>> { Some(Box::new(self.0))}
77146
}
78147
}
79148

80-
#[cfg(test)]
81-
mod test {
82-
// use std::any::TypeId;
149+
// #[cfg(test)]
150+
// mod test {
151+
// // use std::any::TypeId;
83152

84-
use transient::{Transient, TypeId};
153+
// use transient::{Transient, TypeId};
85154

86-
use super::resolution::{Resolve, DefaultTypeErase};
155+
// use super::resolution::{Resolve, DefaultTypeErase};
87156

88-
struct NotAny;
89-
#[derive(Transient)]
90-
struct YesAny;
157+
// struct NotAny;
158+
// #[derive(Transient)]
159+
// struct YesAny;
91160

92-
#[test]
93-
fn check_can_determine() {
94-
let not_any = Resolve::new(NotAny).cast();
95-
assert_eq!(not_any.type_id(), TypeId::of::<()>());
161+
// // #[test]
162+
// // fn check_can_determine() {
163+
// // let not_any = Resolve::new(NotAny).cast();
164+
// // assert_eq!(not_any.type_id(), TypeId::of::<()>());
96165

97-
let yes_any = Resolve::new(YesAny).cast();
98-
assert_ne!(yes_any.type_id(), TypeId::of::<()>());
99-
}
166+
// // let yes_any = Resolve::new(YesAny).cast();
167+
// // assert_ne!(yes_any.type_id(), TypeId::of::<()>());
168+
// // }
100169

101-
// struct HasSentinel<T>(T);
102-
103-
// #[test]
104-
// fn parent_works() {
105-
// let child = resolve!(YesASentinel, HasSentinel<YesASentinel>);
106-
// assert!(child.type_name.ends_with("YesASentinel"));
107-
// assert_eq!(child.parent.unwrap(), TypeId::of::<HasSentinel<YesASentinel>>());
108-
// assert!(child.specialized);
109-
110-
// let not_a_direct_sentinel = resolve!(HasSentinel<YesASentinel>);
111-
// assert!(not_a_direct_sentinel.type_name.contains("HasSentinel"));
112-
// assert!(not_a_direct_sentinel.type_name.contains("YesASentinel"));
113-
// assert!(not_a_direct_sentinel.parent.is_none());
114-
// assert!(!not_a_direct_sentinel.specialized);
115-
// }
116-
}
170+
// // struct HasSentinel<T>(T);
171+
172+
// // #[test]
173+
// // fn parent_works() {
174+
// // let child = resolve!(YesASentinel, HasSentinel<YesASentinel>);
175+
// // assert!(child.type_name.ends_with("YesASentinel"));
176+
// // assert_eq!(child.parent.unwrap(), TypeId::of::<HasSentinel<YesASentinel>>());
177+
// // assert!(child.specialized);
178+
179+
// // let not_a_direct_sentinel = resolve!(HasSentinel<YesASentinel>);
180+
// // assert!(not_a_direct_sentinel.type_name.contains("HasSentinel"));
181+
// // assert!(not_a_direct_sentinel.type_name.contains("YesASentinel"));
182+
// // assert!(not_a_direct_sentinel.parent.is_none());
183+
// // assert!(!not_a_direct_sentinel.specialized);
184+
// // }
185+
// }

core/lib/src/data/capped.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ use crate::response::{self, Responder};
205205
use crate::request::Request;
206206

207207
impl<'r, 'o: 'r, T: Responder<'r, 'o>> Responder<'r, 'o> for Capped<T> {
208-
fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o> {
208+
type Error = T::Error;
209+
fn respond_to(self, request: &'r Request<'_>) -> response::Outcome<'o, Self::Error> {
209210
self.value.respond_to(request)
210211
}
211212
}

core/lib/src/fs/named_file.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use std::ops::{Deref, DerefMut};
44

55
use tokio::fs::{File, OpenOptions};
66

7+
use crate::outcome::try_outcome;
78
use crate::request::Request;
8-
use crate::response::{self, Responder};
9+
use crate::response::{self, Responder, Outcome};
910
use crate::http::ContentType;
1011

1112
/// A [`Responder`] that sends file data with a Content-Type based on its
@@ -152,15 +153,16 @@ impl NamedFile {
152153
/// you would like to stream a file with a different Content-Type than that
153154
/// implied by its extension, use a [`File`] directly.
154155
impl<'r> Responder<'r, 'static> for NamedFile {
155-
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
156-
let mut response = self.1.respond_to(req)?;
156+
type Error = std::convert::Infallible;
157+
fn respond_to(self, req: &'r Request<'_>) -> Outcome<'static, Self::Error> {
158+
let mut response = try_outcome!(self.1.respond_to(req));
157159
if let Some(ext) = self.0.extension() {
158160
if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) {
159161
response.set_header(ct);
160162
}
161163
}
162164

163-
Ok(response)
165+
Outcome::Success(response)
164166
}
165167
}
166168

core/lib/src/fs/server.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ impl Handler for FileServer {
205205

206206
if segments.is_empty() {
207207
let file = NamedFile::open(&self.root).await;
208-
return file.respond_to(req).or_forward((data, Status::NotFound));
208+
return file.respond_to(req).ok().or_forward((data, Status::NotFound, None));
209209
} else {
210210
return Outcome::forward(data, Status::NotFound);
211211
}
@@ -227,19 +227,20 @@ impl Handler for FileServer {
227227

228228
return Redirect::permanent(normal)
229229
.respond_to(req)
230-
.or_forward((data, Status::InternalServerError));
230+
.ok()
231+
.or_forward((data, Status::InternalServerError, None));
231232
}
232233

233234
if !options.contains(Options::Index) {
234235
return Outcome::forward(data, Status::NotFound);
235236
}
236237

237238
let index = NamedFile::open(p.join("index.html")).await;
238-
index.respond_to(req).or_forward((data, Status::NotFound))
239+
index.respond_to(req).ok().or_forward((data, Status::NotFound, None))
239240
},
240241
Some(p) => {
241242
let file = NamedFile::open(p).await;
242-
file.respond_to(req).or_forward((data, Status::NotFound))
243+
file.respond_to(req).ok().or_forward((data, Status::NotFound, None))
243244
}
244245
None => Outcome::forward(data, Status::NotFound),
245246
}

0 commit comments

Comments
 (0)