use std::any::{Any, TypeId};
use std::sync::Arc;
pub trait ProviderIdentity: Send + Sync {
fn provider_name(&self) -> &str;
fn reads_namespace(&self, namespace: &str) -> bool;
fn downcast_any(&self, id: TypeId) -> Option<&(dyn Any + Send + Sync)> {
let _ = id;
None
}
}
pub trait ProviderIdentityExt: ProviderIdentity {
fn downcast_ref<T: Any + Send + Sync>(&self) -> Option<&T> {
self.downcast_any(TypeId::of::<T>())?.downcast_ref()
}
}
impl<P: ProviderIdentity + ?Sized> ProviderIdentityExt for P {}
pub fn expose<T: Any + Send + Sync>(value: &T, id: TypeId) -> Option<&(dyn Any + Send + Sync)> {
(id == TypeId::of::<T>()).then_some(value as &(dyn Any + Send + Sync))
}
impl<T> ProviderIdentity for &T
where
T: ProviderIdentity + ?Sized,
{
fn provider_name(&self) -> &str {
T::provider_name(*self)
}
fn reads_namespace(&self, namespace: &str) -> bool {
T::reads_namespace(*self, namespace)
}
fn downcast_any(&self, id: TypeId) -> Option<&(dyn Any + Send + Sync)> {
T::downcast_any(*self, id)
}
}
impl<T> ProviderIdentity for Box<T>
where
T: ProviderIdentity + ?Sized,
{
fn provider_name(&self) -> &str {
T::provider_name(self.as_ref())
}
fn reads_namespace(&self, namespace: &str) -> bool {
T::reads_namespace(self.as_ref(), namespace)
}
fn downcast_any(&self, id: TypeId) -> Option<&(dyn Any + Send + Sync)> {
T::downcast_any(self.as_ref(), id)
}
}
impl<T> ProviderIdentity for Arc<T>
where
T: ProviderIdentity + ?Sized,
{
fn provider_name(&self) -> &str {
T::provider_name(self.as_ref())
}
fn reads_namespace(&self, namespace: &str) -> bool {
T::reads_namespace(self.as_ref(), namespace)
}
fn downcast_any(&self, id: TypeId) -> Option<&(dyn Any + Send + Sync)> {
T::downcast_any(self.as_ref(), id)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Named;
impl ProviderIdentity for Named {
fn provider_name(&self) -> &str {
"named"
}
fn reads_namespace(&self, namespace: &str) -> bool {
namespace == "named-state"
}
}
struct Exposed {
value: u32,
}
impl ProviderIdentity for Exposed {
fn provider_name(&self) -> &str {
"exposed"
}
fn reads_namespace(&self, _namespace: &str) -> bool {
false
}
fn downcast_any(&self, id: TypeId) -> Option<&(dyn Any + Send + Sync)> {
expose(self, id).or_else(|| expose(&self.value, id))
}
}
#[test]
fn concrete_impl_returns_configured_name() {
assert_eq!(Named.provider_name(), "named");
}
#[test]
fn ref_forwarding_delegates() {
let named = Named;
let borrowed: &Named = &named;
assert_eq!(borrowed.provider_name(), "named");
}
#[test]
fn box_forwarding_delegates() {
let boxed: Box<dyn ProviderIdentity> = Box::new(Named);
assert_eq!(boxed.provider_name(), "named");
}
#[test]
fn arc_forwarding_delegates() {
let arced: Arc<dyn ProviderIdentity> = Arc::new(Named);
assert_eq!(arced.provider_name(), "named");
}
#[test]
fn pointers_forward_reads_namespace() {
let named = Named;
let borrowed: &Named = &named;
let boxed: Box<dyn ProviderIdentity> = Box::new(Named);
let arced: Arc<dyn ProviderIdentity> = Arc::new(Named);
for provider in [borrowed as &dyn ProviderIdentity, &boxed, &arced] {
assert!(provider.reads_namespace("named-state"));
assert!(!provider.reads_namespace("named"));
}
}
#[test]
fn exposed_objects_are_found_through_pointers() {
let exposed = Exposed { value: 7 };
assert_eq!(exposed.downcast_ref::<u32>(), Some(&7));
assert!(exposed.downcast_ref::<Exposed>().is_some());
assert!(exposed.downcast_ref::<String>().is_none());
let borrowed = &exposed;
assert!(
<&Exposed as ProviderIdentity>::downcast_any(&borrowed, TypeId::of::<u32>()).is_some()
);
let arced: Arc<dyn ProviderIdentity> = Arc::new(Exposed { value: 1 });
assert_eq!(arced.downcast_ref::<u32>(), Some(&1));
let boxed: Box<dyn ProviderIdentity> = Box::new(Exposed { value: 2 });
assert_eq!(boxed.downcast_ref::<u32>(), Some(&2));
assert!(Named.downcast_ref::<Named>().is_none());
}
}