#![warn(rust_2018_idioms)]
#![warn(missing_docs)]
#![allow(dead_code)]
mod derived;
mod input;
mod runtime;
pub mod debug;
#[doc(hidden)]
pub mod plumbing;
use crate::plumbing::CycleDetected;
use crate::plumbing::InputQueryStorageOps;
use crate::plumbing::QueryStorageMassOps;
use crate::plumbing::QueryStorageOps;
use crate::plumbing::UncheckedMutQueryStorageOps;
use derive_new::new;
use std::fmt::{self, Debug};
use std::hash::Hash;
pub use crate::runtime::Runtime;
pub use crate::runtime::RuntimeId;
pub trait Database: plumbing::DatabaseStorageTypes + plumbing::DatabaseOps {
fn salsa_runtime(&self) -> &Runtime<Self>;
fn sweep_all(&self, strategy: SweepStrategy) {
self.salsa_runtime().sweep_all(self, strategy);
}
#[allow(unused_variables)]
fn query<Q>(&self, query: Q) -> QueryTable<'_, Self, Q>
where
Q: Query<Self>,
Self: plumbing::GetQueryTable<Q>,
{
<Self as plumbing::GetQueryTable<Q>>::get_query_table(self)
}
#[allow(unused_variables)]
fn query_mut<Q>(&mut self, query: Q) -> QueryTableMut<'_, Self, Q>
where
Q: Query<Self>,
Self: plumbing::GetQueryTable<Q>,
{
<Self as plumbing::GetQueryTable<Q>>::get_query_table_mut(self)
}
fn salsa_event(&self, event_fn: impl Fn() -> Event<Self>) {
#![allow(unused_variables)]
}
fn on_propagated_panic(&self) -> ! {
panic!("concurrent salsa query panicked")
}
}
pub struct Event<DB: Database> {
pub runtime_id: RuntimeId,
pub kind: EventKind<DB>,
}
impl<DB: Database> fmt::Debug for Event<DB> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Event")
.field("runtime_id", &self.runtime_id)
.field("kind", &self.kind)
.finish()
}
}
pub enum EventKind<DB: Database> {
DidValidateMemoizedValue {
database_key: DB::DatabaseKey,
},
WillBlockOn {
other_runtime_id: RuntimeId,
database_key: DB::DatabaseKey,
},
WillChangeInputValue {
database_key: DB::DatabaseKey,
},
WillExecute {
database_key: DB::DatabaseKey,
},
}
impl<DB: Database> fmt::Debug for EventKind<DB> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EventKind::DidValidateMemoizedValue { database_key } => fmt
.debug_struct("DidValidateMemoizedValue")
.field("database_key", database_key)
.finish(),
EventKind::WillBlockOn {
other_runtime_id,
database_key,
} => fmt
.debug_struct("WillBlockOn")
.field("other_runtime_id", other_runtime_id)
.field("database_key", database_key)
.finish(),
EventKind::WillChangeInputValue { database_key } => fmt
.debug_struct("WillChangeInputValue")
.field("database_key", database_key)
.finish(),
EventKind::WillExecute { database_key } => fmt
.debug_struct("WillExecute")
.field("database_key", database_key)
.finish(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SweepStrategy {
keep_values: bool,
}
impl SweepStrategy {
pub fn discard_values(self) -> SweepStrategy {
SweepStrategy {
keep_values: false,
..self
}
}
}
impl Default for SweepStrategy {
fn default() -> Self {
SweepStrategy { keep_values: true }
}
}
pub trait ParallelDatabase: Database + Send {
fn snapshot(&self) -> Snapshot<Self>;
}
#[derive(Debug)]
pub struct Snapshot<DB>
where
DB: ParallelDatabase,
{
db: DB,
}
impl<DB> Snapshot<DB>
where
DB: ParallelDatabase,
{
pub fn new(db: DB) -> Self {
Snapshot { db }
}
}
impl<DB> std::ops::Deref for Snapshot<DB>
where
DB: ParallelDatabase,
{
type Target = DB;
fn deref(&self) -> &DB {
&self.db
}
}
pub trait Query<DB: Database>: Debug + Default + Sized + 'static {
type Key: Clone + Debug + Hash + Eq;
type Value: Clone + Debug;
type Storage: plumbing::QueryStorageOps<DB, Self> + Send + Sync;
type Group: plumbing::QueryGroup<
DB,
GroupStorage = Self::GroupStorage,
GroupKey = Self::GroupKey,
>;
type GroupStorage;
type GroupKey;
fn group_storage(group_storage: &Self::GroupStorage) -> &Self::Storage;
fn group_key(key: Self::Key) -> Self::GroupKey;
}
#[derive(new)]
pub struct QueryTable<'me, DB, Q>
where
DB: plumbing::GetQueryTable<Q>,
Q: Query<DB> + 'me,
{
db: &'me DB,
storage: &'me Q::Storage,
}
impl<DB, Q> QueryTable<'_, DB, Q>
where
DB: plumbing::GetQueryTable<Q>,
Q: Query<DB>,
{
pub fn get(&self, key: Q::Key) -> Q::Value {
let database_key = self.database_key(&key);
self.storage
.try_fetch(self.db, &key, &database_key)
.unwrap_or_else(|CycleDetected| {
self.db
.salsa_runtime()
.report_unexpected_cycle(database_key)
})
}
pub fn sweep(&self, strategy: SweepStrategy)
where
Q::Storage: plumbing::QueryStorageMassOps<DB>,
{
self.storage.sweep(self.db, strategy);
}
fn database_key(&self, key: &Q::Key) -> DB::DatabaseKey {
<DB as plumbing::GetQueryTable<Q>>::database_key(&self.db, key.clone())
}
}
#[derive(new)]
pub struct QueryTableMut<'me, DB, Q>
where
DB: plumbing::GetQueryTable<Q>,
Q: Query<DB> + 'me,
{
db: &'me DB,
storage: &'me Q::Storage,
}
impl<DB, Q> QueryTableMut<'_, DB, Q>
where
DB: plumbing::GetQueryTable<Q>,
Q: Query<DB>,
{
fn database_key(&self, key: &Q::Key) -> DB::DatabaseKey {
<DB as plumbing::GetQueryTable<Q>>::database_key(&self.db, key.clone())
}
pub fn set(&self, key: Q::Key, value: Q::Value)
where
Q::Storage: plumbing::InputQueryStorageOps<DB, Q>,
{
self.storage
.set(self.db, &key, &self.database_key(&key), value);
}
pub fn set_constant(&self, key: Q::Key, value: Q::Value)
where
Q::Storage: plumbing::InputQueryStorageOps<DB, Q>,
{
self.storage
.set_constant(self.db, &key, &self.database_key(&key), value);
}
pub fn set_unchecked(&self, key: Q::Key, value: Q::Value)
where
Q::Storage: plumbing::UncheckedMutQueryStorageOps<DB, Q>,
{
self.storage.set_unchecked(self.db, &key, value);
}
}
#[allow(unused_imports)]
#[macro_use]
extern crate salsa_macros;
#[doc(hidden)]
pub use salsa_macros::*;