shaku_rocket/
inject_provided.rs

1use std::marker::PhantomData;
2use std::ops::Deref;
3
4use rocket::outcome::try_outcome;
5use rocket::request::{FromRequest, Outcome};
6use rocket::{http::Status, Request};
7use shaku::{HasProvider, ModuleInterface};
8
9use crate::get_module_from_state;
10
11/// Used to create a provided service from a shaku `Module`.
12/// The module should be stored in Rocket's state, in a `Box` (It could be
13/// `Box<dyn MyModule>` if the module implementation changes at runtime).
14/// Use this `InjectProvided` struct as a request guard.
15///
16/// # Example
17/// ```rust
18/// #[macro_use] extern crate rocket;
19///
20/// use shaku::{module, Provider};
21/// use shaku_rocket::InjectProvided;
22///
23/// trait HelloWorld {
24///     fn greet(&self) -> String;
25/// }
26///
27/// #[derive(Provider)]
28/// #[shaku(interface = HelloWorld)]
29/// struct HelloWorldImpl;
30///
31/// impl HelloWorld for HelloWorldImpl {
32///     fn greet(&self) -> String {
33///         "Hello, world!".to_owned()
34///     }
35/// }
36///
37/// module! {
38///     HelloModule {
39///         components = [],
40///         providers = [HelloWorldImpl]
41///     }
42/// }
43///
44/// #[get("/")]
45/// fn hello(hello_world: InjectProvided<HelloModule, dyn HelloWorld>) -> String {
46///     hello_world.greet()
47/// }
48///
49/// # fn main() { // We don't actually want to launch the server in an example.
50/// #[rocket::launch]
51/// fn rocket() -> _ {
52///     let module = HelloModule::builder().build();
53///
54///     rocket::build()
55///         .manage(Box::new(module))
56///         .mount("/", routes![hello])
57/// }
58/// # }
59/// ```
60pub struct InjectProvided<M: ModuleInterface + HasProvider<I> + ?Sized, I: ?Sized>(
61    Box<I>,
62    PhantomData<M>,
63);
64
65#[rocket::async_trait]
66impl<'r, M: ModuleInterface + HasProvider<I> + ?Sized, I: ?Sized> FromRequest<'r>
67    for InjectProvided<M, I>
68{
69    type Error = String;
70
71    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
72        let module = try_outcome!(get_module_from_state::<M>(request).await);
73
74        let service_result = module.inner().provide();
75
76        match service_result {
77            Ok(service) => Outcome::Success(InjectProvided(service, PhantomData)),
78            Err(e) => Outcome::Error((Status::InternalServerError, e.to_string())),
79        }
80    }
81}
82
83impl<M: ModuleInterface + HasProvider<I> + ?Sized, I: ?Sized> Deref for InjectProvided<M, I> {
84    type Target = I;
85
86    fn deref(&self) -> &Self::Target {
87        self.0.deref()
88    }
89}