aboutsummaryrefslogtreecommitdiff
path: root/src/di_container/asynchronous/binding/when_configurator.rs
blob: bc8e97ffc90a3bc226c2baa667e676aae6bfdde8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
//! When configurator for a binding for types inside of a [`AsyncDIContainer`].
use std::any::type_name;
use std::marker::PhantomData;

use crate::di_container::BindingOptions;
use crate::errors::async_di_container::AsyncBindingWhenConfiguratorError;
use crate::util::use_double;

use_double!(crate::di_container::asynchronous::AsyncDIContainer);

/// When configurator for a binding for type `Interface` inside a [`AsyncDIContainer`].
pub struct AsyncBindingWhenConfigurator<'di_container, Interface>
where
    Interface: 'static + ?Sized + Send + Sync,
{
    di_container: &'di_container AsyncDIContainer,

    interface_phantom: PhantomData<Interface>,
}

impl<'di_container, Interface> AsyncBindingWhenConfigurator<'di_container, Interface>
where
    Interface: 'static + ?Sized + Send + Sync,
{
    pub(crate) fn new(di_container: &'di_container AsyncDIContainer) -> Self
    {
        Self {
            di_container,
            interface_phantom: PhantomData,
        }
    }

    /// Configures the binding to have a name.
    ///
    /// # Errors
    /// Will return Err if no binding for the interface already exists.
    ///
    /// # Examples
    /// ```
    /// # use syrette::{AsyncDIContainer, injectable};
    /// #
    /// # struct Kitten {}
    /// #
    /// # #[injectable(async = true)]
    /// # impl Kitten
    /// # {
    /// #     fn new() -> Self
    /// #     {
    /// #         Self {}
    /// #     }
    /// # }
    /// #
    /// # #[tokio::main]
    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut di_container = AsyncDIContainer::new();
    ///
    /// di_container
    ///     .bind::<Kitten>()
    ///     .to::<Kitten>()
    ///     .await?
    ///     .in_transient_scope()
    ///     .await
    ///     .when_named("Billy")
    ///     .await?;
    /// #
    /// # Ok(())
    /// # }
    /// ```
    pub async fn when_named(
        self,
        name: &'static str,
    ) -> Result<(), AsyncBindingWhenConfiguratorError>
    {
        let binding = self
            .di_container
            .remove_binding::<Interface>(BindingOptions::new())
            .await
            .map_or_else(
                || {
                    Err(AsyncBindingWhenConfiguratorError::BindingNotFound(
                        type_name::<Interface>(),
                    ))
                },
                Ok,
            )?;

        self.di_container
            .set_binding::<Interface>(BindingOptions::new().name(name), binding)
            .await;

        Ok(())
    }
}

#[cfg(test)]
mod tests
{
    use mockall::predicate::eq;

    use super::*;
    use crate::di_container::asynchronous::MockAsyncDIContainer;
    use crate::provider::r#async::MockIAsyncProvider;
    use crate::test_utils::subjects_async;

    #[tokio::test]
    async fn when_named_works()
    {
        let mut di_container_mock = MockAsyncDIContainer::new();

        di_container_mock
            .expect_remove_binding::<dyn subjects_async::INumber>()
            .with(eq(BindingOptions::new()))
            .return_once(|_name| Some(Box::new(MockIAsyncProvider::new())))
            .once();

        di_container_mock
            .expect_set_binding::<dyn subjects_async::INumber>()
            .withf(|binding_options, _provider| binding_options.name == Some("awesome"))
            .return_once(|_name, _provider| ())
            .once();

        let binding_when_configurator = AsyncBindingWhenConfigurator::<
            dyn subjects_async::INumber,
        >::new(&di_container_mock);

        assert!(binding_when_configurator
            .when_named("awesome")
            .await
            .is_ok());
    }
}