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}