use crate::BaseQueryContext; use crate::CycleDetected; use crate::Query; use crate::QueryStorageOps; use crate::QueryTable; use rustc_hash::FxHashMap; use std::any::Any; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt::Debug; use std::fmt::Display; use std::fmt::Write; use std::hash::Hash; // The master implementation that knits together all the queries // contains a certain amount of boilerplate. This file aims to // reduce that. pub struct MemoizedStorage where Q: Query, QC: BaseQueryContext, { map: RefCell>>, } /// Defines the "current state" of query's memoized results. #[derive(Debug)] pub enum QueryState { /// We are currently computing the result of this query; if we see /// this value in the table, it indeeds a cycle. InProgress, /// We have computed the query already, and here is the result. Memoized(V), } impl Default for MemoizedStorage where Q: Query, QC: BaseQueryContext, { fn default() -> Self { MemoizedStorage { map: RefCell::new(FxHashMap::default()), } } } impl QueryStorageOps for MemoizedStorage where Q: Query, QC: BaseQueryContext, { fn try_fetch<'q>( &self, query: &'q QC, key: &Q::Key, descriptor: impl FnOnce() -> QC::QueryDescriptor, ) -> Result { { let mut map = self.map.borrow_mut(); match map.entry(key.clone()) { Entry::Occupied(entry) => { return match entry.get() { QueryState::InProgress => Err(CycleDetected), QueryState::Memoized(value) => Ok(value.clone()), }; } Entry::Vacant(entry) => { entry.insert(QueryState::InProgress); } } } // If we get here, the query is in progress, and we are the // ones tasked with finding its final value. let descriptor = descriptor(); let value = query.execute_query_implementation::(descriptor, key); { let mut map = self.map.borrow_mut(); let old_value = map.insert(key.clone(), QueryState::Memoized(value.clone())); assert!( match old_value { Some(QueryState::InProgress) => true, _ => false, }, "expected in-progress state, not {:?}", old_value ); } Ok(value) } }