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
use crate::Context;

/// A can construct a [`Service`] which references objects either inside itself or the provided
/// [`Context`].
///
/// Any type implementing the appropriate [`Fn`] trait can be used as a provider:
/// ```
/// # use dfdi::{Service, Context};
/// # use std::convert::Infallible;
/// // A service providing a random number
/// #[derive(Service)]
/// struct Random(u64);
///
/// // Create a context and bind Random to a provider
/// let mut cx = Context::new();
/// cx.bind_fn::<Random>(|_cx: &Context| Random(rand::random()));
///
/// // Print a random number
/// println!("{}", cx.resolve::<Random>().0);
/// ```
pub trait Provider<'cx, S: Service>: 'cx {
    /// Build the output object
    // #! Remember to keep in sync with `ProvideFn`
    fn provide(&'cx self, cx: &'cx Context) -> S::Output<'cx>;
}

/// A pointer to the underlying provider function.
///
/// The first argument must be a pointer, otherwise miri's stacked borrows will reject this code.
/// This is because casting a `*const ()` to, say, a `&'cx ()` resizes the alloc range that the
/// reference has access to down to zero bytes, causing Miri to report undefined behaviour.
///
/// As such, it is up to the caller to ensure that the first argument lives for a lifetime of 'cx.
///
/// # SAFETY
/// - The first argument must have the correct type
/// - The first argument must live for 'cx
// #! This __MUST__ be kept in sync with `Provider::provide` or bad things will happen
pub(crate) type ProvideFn<'cx, S> =
    unsafe fn(*const (), &'cx Context) -> <S as Service>::Output<'cx>;

/// A key to an object that can be created by a [`Provider`] and stored in a [`Context`].
///
/// In most cases, an implementation of this trait is trivial boilerplate, and so it is recommended
/// to use the provided derive macro.
pub trait Service: 'static {
    /// The result of a service resolution
    type Output<'cx>;
}