pub trait Error:
Debug
+ Send
+ Typeable
+ Error { }
Expand description
An extension to std::error::Error which provides dynamic downcasting of errors for use in highly generic contexts.
§When to use this trait
In the vast majority of cases, a library-specific enum
should be used
for cases where there can be many different types of errors. This has
the benefit of being very performant and benefiting from all sorts
of static checking at both the instantiation site and the handling
site of the error.
In other cases, being generic over std::error::Error
may be correct
- usually for logging errors or in other places where an error is used as input.
Now, a motivating example for this trait, which doesn’t fall under either of these cases:
Imagine we are creating a simple web middleware for verifying incoming
HTTP requests. It will take in many different user-defined Verifier
s
and will call them one after the other, rejecting the request on any
error.
The first step would be to write a Verifier
trait:
pub trait Verifier {
/// Verify the request, yielding an error if the request is invalid.
fn verify(&Request) -> Result<(), ???>;
}
A problem quickly arises - what type do we use for the Err
case? We
cannot use a concrete type since each Verifier
may wish to throw
any number of different errors, and we cannot use a generic since
the type is chosen by the implementor, not the caller, and it cannot
be a generic on the trait since we will want to store many Verifier
s
together.
Enter: Box<error::Error>
, a type which can be used to represent
any std::error::Error
with the sufficient bounds, and can also
be handled later by downcasting it to the right error using either
.downcast
or the match_error!
macro. This type can be used to meet
the needs of consumers like Verifier
, but should not be used in cases
where enums or generics are better suited.
Implementations§
Source§impl dyn Error
impl dyn Error
Sourcepub fn downcast<E>(&self) -> Option<&E>where
E: Error,
pub fn downcast<E>(&self) -> Option<&E>where
E: Error,
If this error is E
, downcast this error to E
, by reference.
Examples found in repository?
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
fn main() {
let mut app = rustless::Application::new(rustless::Api::build(|api| {
api.prefix("api");
api.version("v1", rustless::Versioning::Path);
api.mount(swagger::create_api("api-docs"));
api.error_formatter(|err, _media| {
match err.downcast::<UnauthorizedError>() {
Some(_) => {
return Some(rustless::Response::from(
status::StatusCode::Unauthorized,
Box::new("Please provide correct `token` parameter")
))
},
None => None
}
});
api.post("greet/:name", |endpoint| {
endpoint.summary("Sends greeting");
endpoint.desc("Use this to talk to yourself");
endpoint.params(|params| {
params.req_typed("name", json_dsl::string());
params.req_typed("greeting", json_dsl::string());
});
endpoint.handle(|client, params| {
client.text(
format!("{}, {}",
params.find("greeting").unwrap().to_string(),
params.find("name").unwrap().to_string())
)
})
});
api.get("echo", |endpoint| {
endpoint.summary("Sends back what it gets");
endpoint.desc("Use this to talk to yourself");
endpoint.handle(|client, params| {
client.json(params)
})
});
api.namespace("admin", |admin_ns| {
admin_ns.params(|params| {
params.req_typed("token", json_dsl::string())
});
// Using after_validation callback to check token
admin_ns.after_validation(|_client, params| {
match params.find("token") {
// We can unwrap() safely because token in validated already
Some(token) => if token.as_str().unwrap() == "password1" { return Ok(()) },
None => ()
}
// Fire error from callback is token is wrong
return Err(rustless::ErrorResponse{
error: Box::new(UnauthorizedError) as Box<Error + Send>,
response: None
})
});
// This `/api/admin/server_status` endpoint is secure now
admin_ns.get("server_status", |endpoint| {
endpoint.summary("Get server status");
endpoint.desc("Use this API to receive some useful information about the state of our server");
endpoint.handle(|client, _params| {
{
let cookies = client.request.cookies();
#[cfg(feature = "ssl")]
let signed_cookies = cookies.signed();
#[cfg(not(feature = "ssl"))]
let signed_cookies = cookies;
let user_cookie = Cookie::new("session".to_string(), "verified".to_string());
signed_cookies.add(user_cookie);
}
client.text("Everything is OK".to_string())
})
});
})
}));
swagger::enable(&mut app, swagger::Spec {
info: swagger::Info {
title: "Example API".to_string(),
description: Some("Simple API to demonstration".to_string()),
contact: Some(swagger::Contact {
name: "Stanislav Panferov".to_string(),
url: Some("http://panferov.me".to_string()),
..std::default::Default::default()
}),
license: Some(swagger::License {
name: "MIT".to_string(),
url: "http://opensource.org/licenses/MIT".to_string()
}),
..std::default::Default::default()
},
..std::default::Default::default()
});
let mut chain = iron::Chain::new(app);
chain.link(::rustless::batteries::cookie::new("secretsecretsecretsecretsecretsecretsecret".as_bytes()));
iron::Iron::new(chain).http("0.0.0.0:4000").unwrap();
println!("On 4000");
}