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. fn from(v: usize) -> Self { assert!(v < (std::u32::MAX as usize)); Self(v as u32) } /// Convert the ingredient index back into a usize. 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 = dyn Fn(&DB::Jars) -> (&dyn Ingredient) + Send + Sync; /// Like a `DynRoute`, but for `&mut` references. #[allow(type_alias_bounds)] #[allow(unused_parens)] pub type DynMutRoute = dyn Fn(&mut DB::Jars) -> (&mut dyn Ingredient) + 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 { /// 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>, Box>)>, /// Indices of routes which need a 'reset' call. needs_reset: Vec, } impl Routes { /// 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( &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 + 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 { &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 { &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 &mut dyn Ingredient> + '_ { self.needs_reset.iter().map(|&index| self.route_mut(index)) } }