mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-01 17:23:26 +00:00
5ce5e3c374
We need a cheap way to compute field indices.
130 lines
5.1 KiB
Rust
130 lines
5.1 KiB
Rust
use crate::ingredient::IngredientRequiresReset;
|
|
|
|
use super::{ingredient::Ingredient, storage::HasJars};
|
|
|
|
/// An ingredient index identifies a particular [`Ingredient`] in the database.
|
|
/// The database contains a number of jars, and each jar contains a number of ingredients.
|
|
/// Each ingredient is given a unique index as the database is being created.
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
pub struct IngredientIndex(u32);
|
|
|
|
impl IngredientIndex {
|
|
/// Create an ingredient index from a usize.
|
|
pub(crate) fn from(v: usize) -> Self {
|
|
assert!(v < (std::u32::MAX as usize));
|
|
Self(v as u32)
|
|
}
|
|
|
|
pub(crate) fn as_u32(self) -> u32 {
|
|
self.0
|
|
}
|
|
|
|
/// Convert the ingredient index back into a usize.
|
|
pub(crate) fn as_usize(self) -> usize {
|
|
self.0 as usize
|
|
}
|
|
}
|
|
|
|
/// A "route" is a function that, given a `&DB::Jars`, returns an `&dyn Ingredient`.
|
|
/// Routes are constructed (in part) from closures generated by the salsa macros.
|
|
/// These closures look essentially like `|jar| &jar.some_field` -- i.e., if a jar is a struct,
|
|
/// the closure returns a reference to some particular field of the struct
|
|
/// (whichever field has the database for this ingredient).
|
|
///
|
|
/// The key point here is: the struct definitions that are being referencd here come from
|
|
/// crates that consume this crate, and hence we cannot name them directly.
|
|
/// We have to navigate them through closures generated by that downstream crate.
|
|
#[allow(type_alias_bounds)]
|
|
#[allow(unused_parens)]
|
|
pub type DynRoute<DB: HasJars> = dyn Fn(&DB::Jars) -> (&dyn Ingredient<DB>) + Send + Sync;
|
|
|
|
/// Like a `DynRoute`, but for `&mut` references.
|
|
#[allow(type_alias_bounds)]
|
|
#[allow(unused_parens)]
|
|
pub type DynMutRoute<DB: HasJars> =
|
|
dyn Fn(&mut DB::Jars) -> (&mut dyn Ingredient<DB>) + Send + Sync;
|
|
|
|
/// The "routes" structure is used to navigate the database.
|
|
/// The database contains a number of jars, and each jar contains a number of ingredients.
|
|
/// When the database is created, it creates each jar in turn.
|
|
/// Each jar then creates its ingredients.
|
|
/// Each ingredient is registered with the database by invoking the [`Routes::push`] method.
|
|
/// This method assigns it a unique [`IngredientIndex`] and stores some callbacks indicating
|
|
/// how to find the ingredient later based only on the index.
|
|
pub struct Routes<DB: HasJars> {
|
|
/// Vector indexed by ingredient index. Yields the `DynRoute`,
|
|
/// a function which can be applied to the `DB::Jars` to yield
|
|
/// the `dyn Ingredient.
|
|
#[allow(clippy::type_complexity)]
|
|
routes: Vec<(Box<DynRoute<DB>>, Box<DynMutRoute<DB>>)>,
|
|
|
|
/// Indices of routes which need a 'reset' call.
|
|
needs_reset: Vec<IngredientIndex>,
|
|
}
|
|
|
|
impl<DB: HasJars> Routes<DB> {
|
|
/// Construct an empty ingredients listing.
|
|
pub(super) fn new() -> Self {
|
|
Routes {
|
|
routes: vec![],
|
|
needs_reset: vec![],
|
|
}
|
|
}
|
|
|
|
/// Adds a new ingredient into the ingredients table, returning
|
|
/// the `IngredientIndex` that can be used in a `DatabaseKeyIndex`.
|
|
/// This index can then be used to fetch the "route" so that we can
|
|
/// dispatch calls to `maybe_changed_after`.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `requires_reset` -- if true, the [`Ingredient::reset_for_new_revision`] method will be called on this ingredient
|
|
/// at each new revision. See that method for more information.
|
|
/// * `route` -- a closure which, given a database, will identify the ingredient.
|
|
/// This closure will be invoked to dispatch calls to `maybe_changed_after`.
|
|
/// * `mut_route` -- a closure which identifies the ingredient in a mut
|
|
/// database.
|
|
pub fn push<I>(
|
|
&mut self,
|
|
route: impl (Fn(&DB::Jars) -> &I) + Send + Sync + 'static,
|
|
mut_route: impl (Fn(&mut DB::Jars) -> &mut I) + Send + Sync + 'static,
|
|
) -> IngredientIndex
|
|
where
|
|
I: Ingredient<DB> + IngredientRequiresReset + 'static,
|
|
{
|
|
let len = self.routes.len();
|
|
self.routes.push((
|
|
Box::new(move |jars| route(jars)),
|
|
Box::new(move |jars| mut_route(jars)),
|
|
));
|
|
let index = IngredientIndex::from(len);
|
|
|
|
if I::RESET_ON_NEW_REVISION {
|
|
self.needs_reset.push(index);
|
|
}
|
|
|
|
index
|
|
}
|
|
|
|
/// Given an ingredient index, return the "route"
|
|
/// (a function that, given a `&Jars`, returns the ingredient).
|
|
pub fn route(&self, index: IngredientIndex) -> &dyn Fn(&DB::Jars) -> &dyn Ingredient<DB> {
|
|
&self.routes[index.as_usize()].0
|
|
}
|
|
|
|
/// Given an ingredient index, return the "mut route"
|
|
/// (a function that, given an `&mut Jars`, returns the ingredient).
|
|
pub fn route_mut(
|
|
&self,
|
|
index: IngredientIndex,
|
|
) -> &dyn Fn(&mut DB::Jars) -> &mut dyn Ingredient<DB> {
|
|
&self.routes[index.as_usize()].1
|
|
}
|
|
|
|
/// Returns the mut routes for ingredients that need to be reset at the start of each revision.
|
|
pub fn reset_routes(
|
|
&self,
|
|
) -> impl Iterator<Item = &dyn Fn(&mut DB::Jars) -> &mut dyn Ingredient<DB>> + '_ {
|
|
self.needs_reset.iter().map(|&index| self.route_mut(index))
|
|
}
|
|
}
|