expando/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use starnix_sync::Mutex;
use std::any::{Any, TypeId};
use std::collections::BTreeMap;
use std::marker::{Send, Sync};
use std::ops::Deref;
use std::sync::Arc;
/// A spot in an `Expando`.
///
/// Holds a value of type `Arc<T>`.
#[derive(Debug)]
struct ExpandoSlot {
value: Arc<dyn Any + Send + Sync>,
}
impl ExpandoSlot {
fn new(value: Arc<dyn Any + Send + Sync>) -> Self {
ExpandoSlot { value }
}
fn downcast<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
self.value.clone().downcast::<T>().ok()
}
}
/// A lazy collection of values of every type.
///
/// An Expando contains a single instance of every type. The values are instantiated lazily
/// when accessed. Useful for letting modules add their own state to context objects without
/// requiring the context object itself to know about the types in every module.
///
/// Typically the type a module uses in the Expando will be private to that module, which lets
/// the module know that no other code is accessing its slot on the expando.
#[derive(Debug, Default)]
pub struct Expando {
properties: Mutex<BTreeMap<TypeId, ExpandoSlot>>,
}
impl Expando {
/// Get the slot in the expando associated with the given type.
///
/// The slot is added to the expando lazily but the same instance is returned every time the
/// expando is queried for the same type.
pub fn get<T: Any + Send + Sync + Default + 'static>(&self) -> Arc<T> {
let mut properties = self.properties.lock();
let type_id = TypeId::of::<T>();
let slot =
properties.entry(type_id).or_insert_with(|| ExpandoSlot::new(Arc::new(T::default())));
assert_eq!(type_id, slot.value.deref().type_id());
slot.downcast().expect("downcast of expando slot was successful")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Default)]
struct MyStruct {
counter: Mutex<i32>,
}
#[test]
fn basic_test() {
let expando = Expando::default();
let first = expando.get::<MyStruct>();
assert_eq!(*first.counter.lock(), 0);
*first.counter.lock() += 1;
let second = expando.get::<MyStruct>();
assert_eq!(*second.counter.lock(), 1);
}
}