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
use crate::{InjectResult, Injector, Request, RequestInfo};
use std::marker::PhantomData;

/// Lazy request factory allowing requests to be made outside of service
/// creation.
///
/// This lets a service make requests to the injector outside of the service's
/// constructor. For instance, the service can make requests for one of its
/// dependencies at any time, and as many times as it wants, outside of the
/// service's constructor.
///
/// [`Factory`] can be cloned, making it easy to specialize each request made
/// by the factory as needed.
///
/// ## Example
///
/// ```
/// use runtime_injector::{
///     Factory, Injector, IntoSingleton, IntoTransient, Svc,
/// };
///
/// #[derive(Default)]
/// struct Foo;
/// struct Bar(Factory<Box<Foo>>);
///
/// let mut builder = Injector::builder();
/// builder.provide(Foo::default.transient());
/// builder.provide(Bar.singleton());
///
/// let injector = builder.build();
/// let bar: Svc<Bar> = injector.get().unwrap();
/// let _foo1 = bar.0.get().unwrap();
/// let _foo2 = bar.0.get().unwrap();
/// // ...
/// ```
pub struct Factory<R: Request> {
    injector: Injector,
    request_info: RequestInfo,
    marker: PhantomData<fn(&Injector, RequestInfo) -> R>,
}

impl<R: Request> Clone for Factory<R> {
    fn clone(&self) -> Self {
        Factory {
            injector: self.injector.clone(),
            request_info: self.request_info.clone(),
            marker: PhantomData,
        }
    }
}

impl<R: Request> Factory<R> {
    /// Performs the factory's inner request.
    pub fn get(&self) -> InjectResult<R> {
        R::request(&self.injector, &self.request_info)
    }

    /// Gets this factory's inner [`RequestInfo`]. This request info is used by
    /// all requests the factory makes.
    #[must_use]
    pub fn request_info(&self) -> &RequestInfo {
        &self.request_info
    }

    /// Mutably gets this factory's inner [`RequestInfo`]. This request info is
    /// used by all requests the factory makes.
    ///
    /// Modifying this request info affects future requests the factory makes,
    /// meaning additional arguments can be added to requests prior to them
    /// being executed. Since the factory can be cloned, requests can be
    /// specialized by first cloning the factory, then modifying the
    /// [`RequestInfo`] on the clone and using it to make the request instead.
    ///
    /// ## Example
    ///
    /// ```
    /// use runtime_injector::{
    ///     Arg, Factory, InjectResult, Injector, IntoSingleton, IntoTransient,
    ///     Svc, WithArg,
    /// };
    ///
    /// struct Foo(Arg<i32>);
    ///
    /// struct Bar(Factory<Box<Foo>>);
    /// impl Bar {
    ///     fn get_foo(&self, arg: i32) -> InjectResult<Box<Foo>> {
    ///         let mut factory = self.0.clone();
    ///         factory.request_info_mut().with_arg::<Foo, i32>(arg);
    ///         factory.get()
    ///     }
    /// }
    ///
    /// let mut builder = Injector::builder();
    /// builder.provide(Foo.transient());
    /// builder.provide(Bar.singleton());
    ///
    /// let injector = builder.build();
    /// let bar: Svc<Bar> = injector.get().unwrap();
    /// let foo1 = bar.get_foo(1).unwrap();
    /// let foo2 = bar.get_foo(2).unwrap();
    ///
    /// assert_eq!(1, *foo1.0);
    /// assert_eq!(2, *foo2.0);
    /// ```
    #[must_use]
    pub fn request_info_mut(&mut self) -> &mut RequestInfo {
        &mut self.request_info
    }
}

/// Lazy request factory allowing requests to be made outside of service
/// creation.
impl<R: Request> Request for Factory<R> {
    fn request(injector: &Injector, info: &RequestInfo) -> InjectResult<Self> {
        Ok(Factory {
            injector: injector.clone(),
            request_info: info.clone(),
            marker: PhantomData,
        })
    }
}