Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derive a Component implementation that can be injected as Arc<Self> #20

Open
austinjones opened this issue Jan 15, 2021 · 1 comment
Open
Labels
enhancement New feature or request

Comments

@austinjones
Copy link

austinjones commented Jan 15, 2021

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>,
}
@AzureMarker
Copy link
Owner

I have been thinking about extending the derive macro to allow non-trait types (see p0lunin/teloc#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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants