shaku
shaku copied to clipboard
Derive a Component implementation that can be injected as Arc<Self>
One of the first things I tried to do with Shaku was to provide an injected Client instance from the mongodb crate.
I wanted my database code to directly access the Client struct. I couldn't use Provider, because the database driver would be reconnected on each request. And I couldn't do it with #[derive(Component)]
macro because the derive macro requires a trait interface and injects the value as Arc<dyn Trait>
.
I found a solution by implementing a custom Component implementation, which sets the Interface to Self:
use mongodb::Client;
[derive(Debug, Clone)]
pub struct MongoClient(pub Client);
impl MongoClient {
pub fn new() -> anyhow::Result<Self> {
let client = Client::with_options(...)?;
Ok(MongoClient(client))
}
}
impl<M: Module> Component<M> for MongoClient {
type Interface = Self;
type Parameters = ();
fn build(
_context: &mut shaku::ModuleBuildContext<M>,
_params: Self::Parameters,
) -> Box<Self::Interface> {
let runtime: MongoClient = MongoClient::new()
.map_err(|err| Box::new(err))
.expect("DB error");
Box::new(runtime)
}
}
impl Deref for MongoClient {
type Target = Client;
fn deref(&self) -> &Self::Target {
&self.0
}
}
It would be useful if #[derive(Component)]
could generate this code. Perhaps like this:
#[derive(Component, Clone, Debug)]
#[shaku(interface = Self)]
pub struct MongoClient(pub Client);
Then this value would be usable in a Component:
#[derive(Component)]
#[shaku(interface = Database)]
pub struct DatabaseImpl {
#[shaku(inject)]
mongo: Arc<MongoClient>,
}
I have been thinking about extending the derive macro to allow non-trait types (see https://github.com/p0lunin/teloc/issues/8), and it should be pretty easy to allow this.
In your example, you need to perform some initialization (opening the DB connection). That doesn't work well with a derive macro, so it still requires a manual implementation.
Further, you probably want to use a database pool. See the architecture used in the provider guide for an example of this. The DB connection is obtained via provider, which obtains the connection from a DB connection pool (singleton).