Crate dynamic_provider

Source
Expand description

Dynamically request arbitrarily-typed values from providers with borrowed data.

§Motivation

Rust’s Any trait allows for downcasting to access the underlying type of a dyn object. Unfortunately, this is limited to 'static types (types that contain no borrowed data) because there’s no way to distinguish types with different lifetimes by their TypeIds.

This crate provides the dynamically-sized Query type with lifetime parameters and an internal tag indicating what type of value it’s meant to hold, potentially containing those lifetimes.

The Provide and ProvideRef traits supply values to queries, the latter being dyn-capable.

§Prior Art

§Concepts

Lifetime lists

Lists of lifetime variables are represented by types implementing Lt. You can describe a lifetime list with the Lt! macro:

use dynamic_provider::Lt;

/// Lifetime list for retrieving values from `'data` with an argument that
/// borrows `'input`.
type CustomLifetimes<'data, 'input> = Lt!['data, 'input];

/// Prepend a `'local` lifetime to lifetime list `L`.
type PrependLifetime<'local, L> = Lt!['local, ..L];
Type functions

TypeFn implementations describe a type that is parameterized over an arbitrary lifetime list.

A simple type function can be described with the TypeFn! macro:

type RefPair<A, B> = dynamic_provider::TypeFn![for<'a, 'b> (&'a A, &'b B)];
Resource tags

ResourceTag implementations describe how values may be provided to a Query. The trait has two associated TypeFns: Arg, which determines what values are needed to request the resource, and Out, which defines the type of the resource value.

The define_tag! macro provides a convenient way to declare resource tags:

dynamic_provider::define_tag! {
    // syntax:
    // [pub] tag NAME[<...GENERICS>]: [for<...LIFETIMES>] [ARG =>] OUT [where BOUNDS];
    pub tag Field<T>: for<'data, 'key> &'key str => &'data T where T: ?Sized;
    pub tag StatusCode: i32;
}
Providers
[`Provide`][`Provide`]

Supplies values with the lifetime variables in L to the Query object passed to the provide() method.

[`ProvideRef`][`ProvideRef`]
****

Supplies values from a reference to Self with the lifetime of the reference and the lifetime variables in L.

A reference to a Provide implementation (say, &'x impl ProvideRef<L> or &'x mut impl ProvideRef<L>) automatically implements Provide<'x, ..L> for all 'x.

§Examples

§Dynamic field access

use dynamic_provider::{define_tag, request, Lt, Provide, ProvideRef, Query};

define_tag! {
    /// Represents dynamic access to a field.
    ///
    /// The argument of type `&'key str` indicates the name of the field being
    /// accessed.
    pub tag Field<T: ?Sized>: for<'data, 'key> &'key str => &'data T;
}

struct MyData {
    foo: String,
    bar: String,
    baz: Vec<u8>,
}

impl<'key> ProvideRef<Lt!['key]> for MyData {
    fn provide_ref<'data>(&'data self, query: &mut Query<Lt!['data, 'key]>) {
        query
            .put_where::<Field<str>>(|key| *key == "foo", |_| &self.foo)
            .put_where::<Field<str>>(|key| *key == "bar", |_| &self.bar)
            .put_where::<Field<[u8]>>(|key| *key == "baz", |_| &self.baz);
    }
}

/// Dynamically gets a reference to the field with name `key` and type `T`, if
/// provided.
fn get_field<'data, T: ?Sized + 'static>(
    data: &'data dyn for<'key> ProvideRef<Lt!['key]>,
    key: &str,
) -> Option<&'data T> {
    request! { from data;
        tag x: Field<T> where arg <- key => Some(x),
        else => None,
    }
}

// Retrieve fields from a value of type MyData:
let data = MyData {
    foo: "foo!".into(),
    bar: "bar!".into(),
    baz: b"baz!".into(),
};

assert_eq!(get_field::<str>(&data, "foo").unwrap(), "foo!");
assert_eq!(get_field::<str>(&data, "bar").unwrap(), "bar!");
assert_eq!(get_field::<[u8]>(&data, "baz").unwrap(), b"baz!");

// Use () when you need an empty provider:
assert!(get_field::<str>(&(), "foo").is_none());

§Features

§"alloc"

Adds the ProvideBox trait. Adds trait implementations for Rc, Box, and String, and enables additional provided values for std types.

Macros§

LifetimeHkt
Evaluates to a LifetimeHkt implementation.
Lt
Evaluates to an Lt implementation with the given list of lifetime specifiers.
TypeFn
Evaluates to a TypeFn implementation.
define_tag
Declares one or more ResourceTag implementations.
request
Requests one of a set of possible values, refs, or tags from a provider.

Structs§

Mut
A ResourceTag that corresponds to a unique reference to the output value of Tag. Can also be used as an operator on TypeFn implementations.
Query
A type-erased query ready to pass to Provide::provide().
QueryUsing
Packs a context value of type C alongside the query that will be passed to a function fulfilling the query.
Ref
A ResourceTag that corresponds to a shared reference to the output value of Tag. Can also be used as an operator on TypeFn implementations.
TagId
A unique identifier for a specific ResourceTag-implementing type.
Value
A TypeFn that corresponds to T for all lifetime arguments. Can be used as a ResourceTag when T has a fixed size and contains no borrowed data (i.e. T: Sized + 'static).
WhenProvider
Return type of when_provider().

Traits§

LifetimeHkt
Represents a type parameterized by a single lifetime. The LifetimeHkt! macro is used to get an implementation of this trait.
Lt
Represents a list of zero or more lifetime variables
Provide
Provides access to values of arbitrary type.
ProvideBoxalloc
Provides values from a Box.
ProvideRef
Provides access to values of arbitrary type from a reference.
ResourceTag
A marker type used as a key for requesting values from Provide.
TagFor
A ResourceTag that is defined over the given lifetimes.
TypeFn
Represents a type that is parameterized over one or more lifetime variables.

Functions§

for_each_provided_tag_id
Given a provider, calls the given callback for each TagId it provides.
get_provided_tag_ids
Given a provider, creates a collection of TagIds it provides.
provide_by_ref_with
Returns a ProvideRef implementation that delegates to the given function.
provide_with
Returns a Provide implementation that delegates to the given function.
when_provider
Helper function for requesting one of multiple possible values. See also request! and Provide::request().