dfdi_core/traits.rs
1use crate::Context;
2
3/// A can construct a [`Service`] which references objects either inside itself or the provided
4/// [`Context`].
5///
6/// Any type implementing the appropriate [`Fn`] trait can be used as a provider:
7/// ```
8/// # use dfdi::{Service, Context};
9/// # use std::convert::Infallible;
10/// // A service providing a random number
11/// #[derive(Service)]
12/// struct Random(u64);
13///
14/// // Create a context and bind Random to a provider
15/// let mut cx = Context::new();
16/// cx.bind_fn::<Random>(|_cx, _arg| Random(rand::random()));
17///
18/// // Print a random number
19/// println!("{}", cx.resolve::<Random>().0);
20/// ```
21pub trait Provider<'cx, S: Service>: Send + Sync + 'cx {
22 /// Build the output object
23 // #! Remember to keep in sync with `ProvideFn`
24 fn provide(&'cx self, cx: &'cx Context, arg: S::Argument<'_>) -> S::Output<'cx>;
25}
26
27/// A pointer to the underlying provider function.
28///
29/// The first argument must be a pointer, otherwise miri's stacked borrows will reject this code.
30/// This is because casting a `*const ()` to, say, a `&'cx ()` resizes the alloc range that the
31/// reference has access to down to zero bytes, causing Miri to report undefined behaviour.
32///
33/// As such, it is up to the caller to ensure that the first argument lives for a lifetime of 'cx.
34///
35/// # SAFETY
36/// - The first argument must have the correct type
37/// - The first argument must live for 'cx
38// #! This __MUST__ be kept in sync with `Provider::provide` or bad things will happen
39pub(crate) type ProvideFn<'cx, S> =
40 unsafe fn(*const (), &'cx Context, <S as Service>::Argument<'_>) -> <S as Service>::Output<'cx>;
41
42/// A key to an object that can be created by a [`Provider`] and stored in a [`Context`].
43///
44/// In most cases, an implementation of this trait is trivial boilerplate, and so it is recommended
45/// to use the provided derive macro.
46pub trait Service: 'static {
47 /// The result of a service resolution
48 type Output<'cx>;
49
50 /// An argument for the service provider
51 type Argument<'arg>;
52}