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}