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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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<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))
    }
}