supplier/lib.rs
1//! Supply values dynamically.
2//!
3//! API is very unstable. It will likely include a breaking change with any patch version.
4//!
5//! This is intended to replace the `Provider` API that was proposed for the core rust library,
6//! but has been decided to not include.
7use core::any::TypeId;
8use core::fmt;
9
10/// Trait implemented by a type which can dynamically supply values based on type.
11pub trait Supplier {
12 /// Data suppliers should implement this method to supply *all* values they are able to
13 /// supply by using `demand`.
14 ///
15 /// Note that the `supply_*` methods on `Demand` have short-circuit semantics: if an earlier
16 /// method has successfully supplied a value, then later methods will not get an opportunity to
17 /// supply.
18 ///
19 /// # Examples
20 ///
21 /// Supplies a reference to a field with type `String` as a `&str`, and a value of
22 /// type `i32`.
23 ///
24 /// ```rust
25 /// use supplier::{Supplier, Demand};
26 /// # struct SomeConcreteType { field: String, num_field: i32 }
27 ///
28 /// impl Supplier for SomeConcreteType {
29 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
30 /// demand.supply_ref::<str>(&self.field)
31 /// .supply_value::<i32>(self.num_field);
32 /// }
33 /// }
34 /// ```
35 fn supply<'a>(&'a self, demand: &mut Demand<'a>);
36}
37
38/// Request a value from the `Supplier`.
39///
40/// # Examples
41///
42/// Get a string value from a supplier.
43///
44/// ```rust
45/// use supplier::{Supplier, request_value};
46///
47/// fn get_string(supplier: &impl Supplier) -> String {
48/// request_value::<String>(supplier).unwrap()
49/// }
50/// ```
51pub fn request_value<'a, T>(supplier: &'a (impl Supplier + ?Sized)) -> Option<T>
52where
53 T: 'static,
54{
55 request_by_type_tag::<'a, tags::Value<T>>(supplier)
56}
57
58/// Request a reference from the `Supplier`.
59///
60/// # Examples
61///
62/// Get a string reference from a supplier.
63///
64/// ```rust
65/// use supplier::{Supplier, request_ref};
66///
67/// fn get_str(supplier: &impl Supplier) -> &str {
68/// request_ref::<str>(supplier).unwrap()
69/// }
70/// ```
71pub fn request_ref<'a, T>(supplier: &'a (impl Supplier + ?Sized)) -> Option<&'a T>
72where
73 T: 'static + ?Sized,
74{
75 request_by_type_tag::<'a, tags::Ref<tags::MaybeSizedValue<T>>>(supplier)
76}
77
78/// Request a specific value by tag from the `Supplier`.
79fn request_by_type_tag<'a, I>(supplier: &'a (impl Supplier + ?Sized)) -> Option<I::Reified>
80where
81 I: tags::Type<'a>,
82{
83 let mut tagged = TaggedOption::<'a, I>(None);
84 supplier.supply(tagged.as_demand());
85 tagged.0
86}
87
88///////////////////////////////////////////////////////////////////////////////
89// Demand and its methods
90///////////////////////////////////////////////////////////////////////////////
91
92/// A helper object for supplying data by type.
93///
94/// A data supplier supplies values by calling this type's supply methods.
95#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435
96pub struct Demand<'a>(dyn Erased<'a> + 'a);
97
98impl<'a> Demand<'a> {
99 /// Create a new `&mut Demand` from a `&mut dyn Erased` trait object.
100 fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Demand<'a> {
101 // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Demand<'a>` is safe since
102 // `Demand` is repr(transparent).
103 unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Demand<'a>) }
104 }
105
106 /// Supply a value or other type with only static lifetimes.
107 ///
108 /// # Examples
109 ///
110 /// Supplies an `u8`.
111 ///
112 /// ```rust
113 /// use supplier::{Supplier, Demand};
114 /// # struct SomeConcreteType { field: u8 }
115 ///
116 /// impl Supplier for SomeConcreteType {
117 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
118 /// demand.supply_value::<u8>(self.field);
119 /// }
120 /// }
121 /// ```
122 pub fn supply_value<T>(&mut self, value: T) -> &mut Self
123 where
124 T: 'static,
125 {
126 self.supply::<tags::Value<T>>(value)
127 }
128
129 /// Supply a value or other type with only static lifetimes computed using a closure.
130 ///
131 /// # Examples
132 ///
133 /// Supplies a `String` by cloning.
134 ///
135 /// ```rust
136 /// use supplier::{Supplier, Demand};
137 /// # struct SomeConcreteType { field: String }
138 ///
139 /// impl Supplier for SomeConcreteType {
140 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
141 /// demand.supply_value_with::<String>(|| self.field.clone());
142 /// }
143 /// }
144 /// ```
145 pub fn supply_value_with<T>(&mut self, fulfil: impl FnOnce() -> T) -> &mut Self
146 where
147 T: 'static,
148 {
149 self.supply_with::<tags::Value<T>>(fulfil)
150 }
151
152 /// Supply a reference. The referee type must be bounded by `'static`,
153 /// but may be unsized.
154 ///
155 /// # Examples
156 ///
157 /// Supplies a reference to a field as a `&str`.
158 ///
159 /// ```rust
160 /// use supplier::{Supplier, Demand};
161 /// # struct SomeConcreteType { field: String }
162 ///
163 /// impl Supplier for SomeConcreteType {
164 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
165 /// demand.supply_ref::<str>(&self.field);
166 /// }
167 /// }
168 /// ```
169 pub fn supply_ref<T: ?Sized + 'static>(&mut self, value: &'a T) -> &mut Self {
170 self.supply::<tags::Ref<tags::MaybeSizedValue<T>>>(value)
171 }
172
173 /// Supply a reference computed using a closure. The referee type
174 /// must be bounded by `'static`, but may be unsized.
175 ///
176 /// # Examples
177 ///
178 /// Supplies a reference to a field as a `&str`.
179 ///
180 /// ```rust
181 /// use supplier::{Supplier, Demand};
182 /// # struct SomeConcreteType { business: String, party: String }
183 /// # fn today_is_a_weekday() -> bool { true }
184 ///
185 /// impl Supplier for SomeConcreteType {
186 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
187 /// demand.supply_ref_with::<str>(|| {
188 /// if today_is_a_weekday() {
189 /// &self.business
190 /// } else {
191 /// &self.party
192 /// }
193 /// });
194 /// }
195 /// }
196 /// ```
197 pub fn supply_ref_with<T: ?Sized + 'static>(
198 &mut self,
199 fulfil: impl FnOnce() -> &'a T,
200 ) -> &mut Self {
201 self.supply_with::<tags::Ref<tags::MaybeSizedValue<T>>>(fulfil)
202 }
203
204 /// Supply a value with the given `Type` tag.
205 fn supply<I>(&mut self, value: I::Reified) -> &mut Self
206 where
207 I: tags::Type<'a>,
208 {
209 if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::<I>() {
210 res.0 = Some(value);
211 }
212 self
213 }
214
215 /// Supply a value with the given `Type` tag, using a closure to prevent unnecessary work.
216 fn supply_with<I>(&mut self, fulfil: impl FnOnce() -> I::Reified) -> &mut Self
217 where
218 I: tags::Type<'a>,
219 {
220 if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::<I>() {
221 res.0 = Some(fulfil());
222 }
223 self
224 }
225
226 /// Check if the `Demand` would be satisfied if supplied with a
227 /// value of the specified type. If the type does not match or has
228 /// already been supplied, returns false.
229 ///
230 /// # Examples
231 ///
232 /// Check if an `u8` still needs to be supplied and then supplies
233 /// it.
234 ///
235 /// ```rust
236 /// use supplier::{Supplier, Demand};
237 ///
238 /// struct Parent(Option<u8>);
239 ///
240 /// impl Supplier for Parent {
241 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
242 /// if let Some(v) = self.0 {
243 /// demand.supply_value::<u8>(v);
244 /// }
245 /// }
246 /// }
247 ///
248 /// struct Child {
249 /// parent: Parent,
250 /// }
251 ///
252 /// impl Child {
253 /// // Pretend that this takes a lot of resources to evaluate.
254 /// fn an_expensive_computation(&self) -> Option<u8> {
255 /// Some(99)
256 /// }
257 /// }
258 ///
259 /// impl Supplier for Child {
260 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
261 /// // In general, we don't know if this call will supply
262 /// // an `u8` value or not...
263 /// self.parent.supply(demand);
264 ///
265 /// // ...so we check to see if the `u8` is needed before
266 /// // we run our expensive computation.
267 /// if demand.would_be_satisfied_by_value_of::<u8>() {
268 /// if let Some(v) = self.an_expensive_computation() {
269 /// demand.supply_value::<u8>(v);
270 /// }
271 /// }
272 ///
273 /// // The demand will be satisfied now, regardless of if
274 /// // the parent supplied the value or we did.
275 /// assert!(!demand.would_be_satisfied_by_value_of::<u8>());
276 /// }
277 /// }
278 ///
279 /// let parent = Parent(Some(42));
280 /// let child = Child { parent };
281 /// assert_eq!(Some(42), supplier::request_value::<u8>(&child));
282 ///
283 /// let parent = Parent(None);
284 /// let child = Child { parent };
285 /// assert_eq!(Some(99), supplier::request_value::<u8>(&child));
286 /// ```
287 pub fn would_be_satisfied_by_value_of<T>(&self) -> bool
288 where
289 T: 'static,
290 {
291 self.would_be_satisfied_by::<tags::Value<T>>()
292 }
293
294 /// Check if the `Demand` would be satisfied if supplied with a
295 /// reference to a value of the specified type. If the type does
296 /// not match or has already been supplied, returns false.
297 ///
298 /// # Examples
299 ///
300 /// Check if a `&str` still needs to be supplied and then supplies
301 /// it.
302 ///
303 /// ```rust
304 /// use supplier::{Supplier, Demand};
305 ///
306 /// struct Parent(Option<String>);
307 ///
308 /// impl Supplier for Parent {
309 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
310 /// if let Some(v) = &self.0 {
311 /// demand.supply_ref::<str>(v);
312 /// }
313 /// }
314 /// }
315 ///
316 /// struct Child {
317 /// parent: Parent,
318 /// name: String,
319 /// }
320 ///
321 /// impl Child {
322 /// // Pretend that this takes a lot of resources to evaluate.
323 /// fn an_expensive_computation(&self) -> Option<&str> {
324 /// Some(&self.name)
325 /// }
326 /// }
327 ///
328 /// impl Supplier for Child {
329 /// fn supply<'a>(&'a self, demand: &mut Demand<'a>) {
330 /// // In general, we don't know if this call will supply
331 /// // a `str` reference or not...
332 /// self.parent.supply(demand);
333 ///
334 /// // ...so we check to see if the `&str` is needed before
335 /// // we run our expensive computation.
336 /// if demand.would_be_satisfied_by_ref_of::<str>() {
337 /// if let Some(v) = self.an_expensive_computation() {
338 /// demand.supply_ref::<str>(v);
339 /// }
340 /// }
341 ///
342 /// // The demand will be satisfied now, regardless of if
343 /// // the parent supplied the reference or we did.
344 /// assert!(!demand.would_be_satisfied_by_ref_of::<str>());
345 /// }
346 /// }
347 ///
348 /// let parent = Parent(Some("parent".into()));
349 /// let child = Child { parent, name: "child".into() };
350 /// assert_eq!(Some("parent"), supplier::request_ref::<str>(&child));
351 ///
352 /// let parent = Parent(None);
353 /// let child = Child { parent, name: "child".into() };
354 /// assert_eq!(Some("child"), supplier::request_ref::<str>(&child));
355 /// ```
356 pub fn would_be_satisfied_by_ref_of<T>(&self) -> bool
357 where
358 T: ?Sized + 'static,
359 {
360 self.would_be_satisfied_by::<tags::Ref<tags::MaybeSizedValue<T>>>()
361 }
362
363 fn would_be_satisfied_by<I>(&self) -> bool
364 where
365 I: tags::Type<'a>,
366 {
367 matches!(self.0.downcast::<I>(), Some(TaggedOption(None)))
368 }
369}
370impl<'a> fmt::Debug for Demand<'a> {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 f.debug_struct("Demand").finish_non_exhaustive()
373 }
374}
375///////////////////////////////////////////////////////////////////////////////
376// Type tags
377///////////////////////////////////////////////////////////////////////////////
378
379mod tags {
380 //! Type tags are used to identify a type using a separate value. This module includes type tags
381 //! for some very common types.
382 //!
383 //! Currently type tags are not exposed to the user. But in the future, if you want to use the
384 //! Supplier API with more complex types (typically those including lifetime parameters), you
385 //! will need to write your own tags.
386
387 use core::marker::PhantomData;
388
389 /// This trait is implemented by specific tag types in order to allow
390 /// describing a type which can be requested for a given lifetime `'a`.
391 ///
392 /// A few example implementations for type-driven tags can be found in this
393 /// module, although crates may also implement their own tags for more
394 /// complex types with internal lifetimes.
395 pub trait Type<'a>: Sized + 'static {
396 /// The type of values which may be tagged by this tag for the given
397 /// lifetime.
398 type Reified: 'a;
399 }
400
401 /// Similar to the [`Type`] trait, but represents a type which may be unsized (i.e., has a
402 /// `?Sized` bound). E.g., `str`.
403 pub trait MaybeSizedType<'a>: Sized + 'static {
404 type Reified: 'a + ?Sized;
405 }
406
407 impl<'a, T: Type<'a>> MaybeSizedType<'a> for T {
408 type Reified = T::Reified;
409 }
410
411 /// Type-based tag for types bounded by `'static`, i.e., with no borrowed elements.
412 #[derive(Debug)]
413 pub struct Value<T: 'static>(PhantomData<T>);
414
415 impl<'a, T: 'static> Type<'a> for Value<T> {
416 type Reified = T;
417 }
418
419 /// Type-based tag similar to [`Value`] but which may be unsized (i.e., has a `?Sized` bound).
420 #[derive(Debug)]
421 pub struct MaybeSizedValue<T: ?Sized + 'static>(PhantomData<T>);
422
423 impl<'a, T: ?Sized + 'static> MaybeSizedType<'a> for MaybeSizedValue<T> {
424 type Reified = T;
425 }
426
427 /// Type-based tag for reference types (`&'a T`, where T is represented by
428 /// `<I as MaybeSizedType<'a>>::Reified`.
429 #[derive(Debug)]
430 pub struct Ref<I>(PhantomData<I>);
431
432 impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref<I> {
433 type Reified = &'a I::Reified;
434 }
435}
436
437/// An `Option` with a type tag `I`.
438///
439/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed
440/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically
441/// checked for the concrete type, there is some degree of type safety.
442#[repr(transparent)]
443struct TaggedOption<'a, I: tags::Type<'a>>(Option<I::Reified>);
444
445impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> {
446 fn as_demand(&mut self) -> &mut Demand<'a> {
447 Demand::new(self as &mut (dyn Erased<'a> + 'a))
448 }
449}
450
451/// Represents a type-erased but identifiable object.
452///
453/// This trait is exclusively implemented by the `TaggedOption` type.
454unsafe trait Erased<'a>: 'a {
455 /// The `TypeId` of the erased type.
456 fn tag_id(&self) -> TypeId;
457}
458
459unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {
460 fn tag_id(&self) -> TypeId {
461 TypeId::of::<I>()
462 }
463}
464
465impl<'a> dyn Erased<'a> + 'a {
466 /// Returns some reference to the dynamic value if it is tagged with `I`,
467 /// or `None` otherwise.
468 #[inline]
469 fn downcast<I>(&self) -> Option<&TaggedOption<'a, I>>
470 where
471 I: tags::Type<'a>,
472 {
473 if self.tag_id() == TypeId::of::<I>() {
474 // SAFETY: Just checked whether we're pointing to an I.
475 Some(unsafe { &*(self as *const Self).cast::<TaggedOption<'a, I>>() })
476 } else {
477 None
478 }
479 }
480
481 /// Returns some mutable reference to the dynamic value if it is tagged with `I`,
482 /// or `None` otherwise.
483 #[inline]
484 fn downcast_mut<I>(&mut self) -> Option<&mut TaggedOption<'a, I>>
485 where
486 I: tags::Type<'a>,
487 {
488 if self.tag_id() == TypeId::of::<I>() {
489 // SAFETY: Just checked whether we're pointing to an I.
490 Some(unsafe { &mut *(self as *mut Self).cast::<TaggedOption<'a, I>>() })
491 } else {
492 None
493 }
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use crate::{request_ref, Supplier};
500
501 struct Container {
502 x: u8,
503 y: String,
504 z: Vec<u8>,
505 }
506
507 impl Supplier for Container {
508 fn supply<'a>(&'a self, demand: &mut crate::Demand<'a>) {
509 demand
510 .supply_ref(&self.x)
511 .supply_ref(&*self.y)
512 .supply_ref(&*self.z);
513 }
514 }
515
516 #[test]
517 fn it_works() {
518 let c = Container {
519 x: 42,
520 y: "foobar".to_string(),
521 z: vec![1, 2, 3],
522 };
523
524 assert_eq!(request_ref::<u8>(&c).unwrap(), &42);
525 assert_eq!(request_ref::<str>(&c).unwrap(), "foobar");
526 assert_eq!(request_ref::<[u8]>(&c).unwrap(), &[1, 2, 3]);
527 }
528}