object_provider/
lib.rs

1#![cfg_attr(not(test), no_std)]
2//! Trait for requesting values by type from a given object.
3//!
4//! # Examples
5//!
6//! ## Using a Provider
7//!
8//! ```
9//! # use object_provider::*;
10//! # use std::path::{Path, PathBuf};
11//! # use std::fmt::Debug;
12//! # use std::pin::Pin;
13//! # struct MyProvider {
14//! #     path: PathBuf,
15//! # }
16//! # impl ObjectProvider for MyProvider {
17//! #     fn provide<'a>(&'a self, request: Pin<&mut Request<'a>>) {
18//! #         request
19//! #             .provide_ref::<PathBuf>(&self.path)
20//! #             .provide_ref::<Path>(&self.path)
21//! #             .provide_ref::<dyn Debug>(&self.path);
22//! #     }
23//! # }
24//! # let my_path = Path::new("hello/world");
25//! # let my_provider = MyProvider { path: my_path.to_owned() };
26//! let provider: &dyn ObjectProvider;
27//! # provider = &my_provider;
28//!
29//! // It's possible to request concrete types like `PathBuf`
30//! let path_buf = provider.request_ref::<PathBuf>().unwrap();
31//! assert_eq!(path_buf, my_path);
32//!
33//! // Requesting `!Sized` types, like slices and trait objects, is also supported.
34//! let path = provider.request_ref::<Path>().unwrap();
35//! assert_eq!(path, my_path);
36//!
37//! let debug = provider.request_ref::<dyn Debug>().unwrap();
38//! assert_eq!(
39//!     format!("{:?}", debug),
40//!     format!("{:?}", my_path),
41//! );
42//!
43//! // Types or interfaces not explicitly provided return `None`.
44//! assert!(provider.request_ref::<i32>().is_none());
45//! assert!(provider.request_ref::<dyn AsRef<Path>>().is_none());
46//! ```
47//!
48//! ## Implementing a Provider
49//!
50//! ```
51//! # use object_provider::*;
52//! # use std::path::{Path, PathBuf};
53//! # use std::fmt::Debug;
54//! # use std::pin::Pin;
55//! struct MyProvider {
56//!     path: PathBuf,
57//! }
58//!
59//! impl ObjectProvider for MyProvider {
60//!     fn provide<'a>(&'a self, request: Pin<&mut Request<'a>>) {
61//!         request
62//!             .provide_ref::<PathBuf>(&self.path)
63//!             .provide_ref::<Path>(&self.path)
64//!             .provide_ref::<dyn Debug>(&self.path);
65//!     }
66//! }
67//! ```
68
69use core::any::TypeId;
70use core::fmt;
71use core::marker::{PhantomData, PhantomPinned};
72use core::pin::Pin;
73
74struct ReqRef<T: ?Sized + 'static>(&'static T);
75struct ReqVal<T: 'static>(T);
76
77/// A dynamic request for an object based on its type.
78#[repr(C)]
79pub struct Request<'a> {
80    type_id: TypeId,
81    _pinned: PhantomPinned,
82    _marker: PhantomData<&'a ()>,
83}
84
85impl<'a> Request<'a> {
86    /// Provides a reference of type `&'a T` in response to this request.
87    ///
88    /// If a reference of type `&'a T` has already been provided for this
89    /// request, the existing value will be replaced by the newly provided
90    /// value.
91    ///
92    /// This method can be chained within `provide` implementations to concisely
93    /// provide multiple objects.
94    pub fn provide_ref<T: ?Sized + 'static>(self: Pin<&mut Self>, value: &'a T) -> Pin<&mut Self> {
95        self.provide_ref_with(|| value)
96    }
97
98    /// Lazily provides a reference of type `&'a T` in response to this request.
99    ///
100    /// If a reference of type `&'a T` has already been provided for this
101    /// request, the existing value will be replaced by the newly provided
102    /// value.
103    ///
104    /// The passed closure is only called if the value will be successfully
105    /// provided.
106    ///
107    /// This method can be chained within `provide` implementations to concisely
108    /// provide multiple objects.
109    pub fn provide_ref_with<T: ?Sized + 'static, F>(
110        mut self: Pin<&mut Self>,
111        cb: F,
112    ) -> Pin<&mut Self>
113    where
114        F: FnOnce() -> &'a T,
115    {
116        if self.is_ref::<T>() {
117            // safety: `self.is_ref::<T>()` ensured the data field is `&'a T`.
118            unsafe {
119                *self.as_mut().downcast_unchecked::<&'a T>() = Some(cb());
120            }
121        }
122        self
123    }
124
125    /// Provides an value of type `T` in response to this request.
126    ///
127    /// If a value of type `T` has already been provided for this request, the
128    /// existing value will be replaced by the newly provided value.
129    ///
130    /// This method can be chained within `provide` implementations to concisely
131    /// provide multiple objects.
132    pub fn provide_value<T: 'static>(self: Pin<&mut Self>, value: T) -> Pin<&mut Self> {
133        self.provide_value_with(|| value)
134    }
135
136    /// Lazily provides a value of type `T` in response to this request.
137    ///
138    /// If a value of type `T` has already been provided for this request, the
139    /// existing value will be replaced by the newly provided value.
140    ///
141    /// The passed closure is only called if the value will be successfully
142    /// provided.
143    ///
144    /// This method can be chained within `provide` implementations to concisely
145    /// provide multiple objects.
146    pub fn provide_value_with<T: 'static, F>(mut self: Pin<&mut Self>, cb: F) -> Pin<&mut Self>
147    where
148        F: FnOnce() -> T,
149    {
150        if self.is_value::<T>() {
151            // safety: `self.is_value::<T>()` ensured the data field is `T`.
152            unsafe {
153                *self.as_mut().downcast_unchecked::<T>() = Some(cb());
154            }
155        }
156        self
157    }
158
159    /// Returns `true` if the requested type is `&'a T`
160    pub fn is_ref<T: ?Sized + 'static>(&self) -> bool {
161        self.type_id == TypeId::of::<ReqRef<T>>()
162    }
163
164    /// Returns `true` if the requested type is `T`
165    pub fn is_value<T: 'static>(&self) -> bool {
166        self.type_id == TypeId::of::<ReqVal<T>>()
167    }
168
169    // internal implementation detail - performs an unchecked downcast.
170    unsafe fn downcast_unchecked<T>(self: Pin<&mut Self>) -> &mut Option<T> {
171        let ptr = self.get_unchecked_mut() as *mut Self as *mut RequestBuf<'a, T>;
172        &mut (*ptr).value
173    }
174
175    /// Calls the provided closure with a request for the the type `&'a T`,
176    /// returning `Some(&T)` if the request was fulfilled, and `None` otherwise.
177    ///
178    /// The `ObjectProviderExt` trait provides helper methods specifically for
179    /// types implementing `ObjectProvider`.
180    pub fn request_ref<T: ?Sized + 'static, F>(f: F) -> Option<&'a T>
181    where
182        F: FnOnce(Pin<&mut Request<'a>>),
183    {
184        let mut buf = RequestBuf::for_ref();
185        // safety: We never move `buf` after creating `pinned`.
186        let mut pinned = unsafe { Pin::new_unchecked(&mut buf) };
187        f(pinned.as_mut().request());
188        pinned.take()
189    }
190
191    /// Calls the provided closure with a request for the the type `T`,
192    /// returning `Some(T)` if the request was fulfilled, and `None` otherwise.
193    ///
194    /// The `ObjectProviderExt` trait provides helper methods specifically for
195    /// types implementing `ObjectProvider`.
196    pub fn request_value<T: 'static, F>(f: F) -> Option<T>
197    where
198        F: FnOnce(Pin<&mut Request<'a>>),
199    {
200        let mut buf = RequestBuf::for_value();
201        // safety: We never move `buf` after creating `pinned`.
202        let mut pinned = unsafe { Pin::new_unchecked(&mut buf) };
203        f(pinned.as_mut().request());
204        pinned.take()
205    }
206}
207
208impl<'a> fmt::Debug for Request<'a> {
209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210        f.debug_struct("Request")
211            .field("type_id", &self.type_id)
212            .finish()
213    }
214}
215
216/// Low level buffer API used to create typed object requests.
217///
218/// Due to a heavy dependency on [`Pin`], this type is inconvenient to use
219/// directly. Prefer using the [`ObjectProviderExt`] trait and [`Request::with`]
220/// APIs when possible.
221// Needs to have a known layout so we can do unsafe pointer shenanigans.
222#[repr(C)]
223#[derive(Debug)]
224struct RequestBuf<'a, T> {
225    request: Request<'a>,
226    value: Option<T>,
227}
228
229impl<'a, T: ?Sized + 'static> RequestBuf<'a, &'a T> {
230    /// Create a new `RequestBuf` object.
231    ///
232    /// This type must be pinned before it can be used.
233    fn for_ref() -> Self {
234        // safety: ReqRef is a marker type for `&'a T`
235        unsafe { Self::new_internal(TypeId::of::<ReqRef<T>>()) }
236    }
237}
238
239impl<'a, T: 'static> RequestBuf<'a, T> {
240    /// Create a new `RequestBuf` object.
241    ///
242    /// This type must be pinned before it can be used.
243    fn for_value() -> Self {
244        // safety: ReqVal is a marker type for `T`
245        unsafe { Self::new_internal(TypeId::of::<ReqVal<T>>()) }
246    }
247}
248
249impl<'a, T> RequestBuf<'a, T> {
250    unsafe fn new_internal(type_id: TypeId) -> Self {
251        RequestBuf {
252            request: Request {
253                type_id,
254                _pinned: PhantomPinned,
255                _marker: PhantomData,
256            },
257            value: None,
258        }
259    }
260
261    /// Get the untyped `Request` reference for this `RequestBuf`.
262    fn request(self: Pin<&mut Self>) -> Pin<&mut Request<'a>> {
263        // safety: projecting Pin onto our `request` field.
264        unsafe { self.map_unchecked_mut(|this| &mut this.request) }
265    }
266
267    /// Take a value previously provided to this `RequestBuf`.
268    fn take(self: Pin<&mut Self>) -> Option<T> {
269        // safety: we don't project Pin onto our `value` field.
270        unsafe { self.get_unchecked_mut().value.take() }
271    }
272}
273
274/// Trait to provide other objects based on a requested type at runtime.
275///
276/// See also the [`ObjectProviderExt`] trait which provides the `request` method.
277pub trait ObjectProvider {
278    /// Provide an object of a given type in response to an untyped request.
279    fn provide<'a>(&'a self, request: Pin<&mut Request<'a>>);
280}
281
282/// Methods supported by all [`ObjectProvider`] implementors.
283pub trait ObjectProviderExt {
284    /// Request a reference of type `&T` from an object provider.
285    fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T>;
286
287    /// Request an owned value of type `T` from an object provider.
288    fn request_value<T: 'static>(&self) -> Option<T>;
289}
290
291impl<O: ?Sized + ObjectProvider> ObjectProviderExt for O {
292    fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
293        Request::request_ref::<T, _>(|req| self.provide(req))
294    }
295
296    fn request_value<T: 'static>(&self) -> Option<T> {
297        Request::request_value::<T, _>(|req| self.provide(req))
298    }
299}
300
301#[cfg(test)]
302mod test {
303    use super::*;
304    use std::path::{Path, PathBuf};
305
306    #[test]
307    fn basic_context() {
308        struct HasContext {
309            int: i32,
310            path: PathBuf,
311        }
312        impl ObjectProvider for HasContext {
313            fn provide<'a>(&'a self, request: Pin<&mut Request<'a>>) {
314                request
315                    .provide_ref::<i32>(&self.int)
316                    .provide_ref::<Path>(&self.path)
317                    .provide_ref::<dyn fmt::Display>(&self.int)
318                    .provide_value::<i32>(self.int);
319            }
320        }
321
322        let provider: &dyn ObjectProvider = &HasContext {
323            int: 10,
324            path: PathBuf::new(),
325        };
326
327        assert_eq!(provider.request_ref::<i32>(), Some(&10));
328        assert_eq!(provider.request_value::<i32>(), Some(10));
329        assert!(provider.request_ref::<u32>().is_none());
330        assert_eq!(
331            provider
332                .request_ref::<dyn fmt::Display>()
333                .map(|d| d.to_string()),
334            Some("10".to_owned())
335        );
336        assert!(provider.request_ref::<dyn fmt::Debug>().is_none());
337        assert_eq!(provider.request_ref::<Path>(), Some(Path::new("")));
338    }
339}