mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-08-22 11:34:52 +00:00
wip
This commit is contained in:
parent
2bb67d76b3
commit
cb8192b82e
11 changed files with 227 additions and 331 deletions
|
@ -38,7 +38,6 @@ compact_str = { version = "0.9", optional = true }
|
|||
shuttle = { version = "0.8.1", optional = true }
|
||||
|
||||
# Persistent caching
|
||||
erased-serde = { version = "0.4.6", optional = true }
|
||||
serde = { version = "1.0.219", features = ["derive"], optional = true }
|
||||
|
||||
[features]
|
||||
|
@ -46,7 +45,6 @@ default = ["salsa_unstable", "rayon", "macros", "inventory", "accumulator", "per
|
|||
inventory = ["dep:inventory"]
|
||||
persistence = [
|
||||
"dep:serde",
|
||||
"dep:erased-serde",
|
||||
"salsa-macros/persistence",
|
||||
"thin-vec/serde",
|
||||
]
|
||||
|
|
|
@ -201,7 +201,12 @@ macro_rules! setup_interned_struct {
|
|||
|
||||
|
||||
impl $Configuration {
|
||||
pub fn ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self> {
|
||||
pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<Self> {
|
||||
Self::ingredient_(db.zalsa())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ingredient_(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self> {
|
||||
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
|
||||
$zalsa::IngredientCache::new();
|
||||
|
||||
|
@ -250,7 +255,7 @@ macro_rules! setup_interned_struct {
|
|||
zalsa: &$zalsa::Zalsa
|
||||
) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
|
||||
let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
|
||||
<$Configuration>::ingredient(zalsa).entries(zalsa).map(|(key, _)| key)
|
||||
<$Configuration>::ingredient_(zalsa).entries(zalsa).map(|(key, _)| key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -316,7 +321,7 @@ macro_rules! setup_interned_struct {
|
|||
)*
|
||||
{
|
||||
let (zalsa, zalsa_local) = db.zalsas();
|
||||
$Configuration::ingredient(zalsa).intern(zalsa, zalsa_local,
|
||||
$Configuration::ingredient_(zalsa).intern(zalsa, zalsa_local,
|
||||
StructKey::<$db_lt>($($field_id,)* std::marker::PhantomData::default()), |_, data| ($($zalsa::interned::Lookup::into_owned(data.$field_index),)*))
|
||||
}
|
||||
|
||||
|
@ -328,7 +333,7 @@ macro_rules! setup_interned_struct {
|
|||
$Db: ?Sized + $zalsa::Database,
|
||||
{
|
||||
let zalsa = db.zalsa();
|
||||
let fields = $Configuration::ingredient(zalsa).fields(zalsa, self);
|
||||
let fields = $Configuration::ingredient_(zalsa).fields(zalsa, self);
|
||||
$zalsa::return_mode_expression!(
|
||||
$field_option,
|
||||
$field_ty,
|
||||
|
@ -351,7 +356,7 @@ macro_rules! setup_interned_struct {
|
|||
{
|
||||
$zalsa::with_attached_database(|db| {
|
||||
let zalsa = db.zalsa();
|
||||
let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
|
||||
let fields = $Configuration::ingredient_(zalsa).fields(zalsa, this);
|
||||
let mut f = f.debug_struct(stringify!($Struct));
|
||||
$(
|
||||
let f = f.field(stringify!($field_id), &fields.$field_index);
|
||||
|
@ -375,7 +380,7 @@ macro_rules! setup_interned_struct {
|
|||
{
|
||||
$zalsa::with_attached_database(|db| {
|
||||
let zalsa = db.zalsa();
|
||||
let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
|
||||
let fields = $Configuration::ingredient_(zalsa).fields(zalsa, this);
|
||||
let mut f = f.debug_struct(stringify!($Struct));
|
||||
$(
|
||||
let f = f.field(stringify!($field_id), &fields.$field_index);
|
||||
|
|
|
@ -105,7 +105,7 @@ macro_rules! setup_tracked_fn {
|
|||
|
||||
type Ingredient = $zalsa::function::IngredientImpl<$Configuration>;
|
||||
fn ingredient(db: &dyn $zalsa::Database) -> &Self::Ingredient {
|
||||
$Configuration::fn_ingredient(db)
|
||||
$Configuration::uninit_fn_ingredient(db.zalsa())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,12 +233,17 @@ macro_rules! setup_tracked_fn {
|
|||
|
||||
#[inline]
|
||||
fn fn_ingredient_<'z>(db: &dyn $Db, zalsa: &'z $zalsa::Zalsa) -> &'z $zalsa::function::IngredientImpl<$Configuration> {
|
||||
Self::uninit_fn_ingredient(zalsa)
|
||||
.get_or_init(|| *<dyn $Db as $Db>::zalsa_register_downcaster(db))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn uninit_fn_ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa::function::IngredientImpl<$Configuration> {
|
||||
// SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the first
|
||||
// ingredient created by our jar is the function ingredient.
|
||||
unsafe {
|
||||
$FN_CACHE.get_or_create(zalsa, || zalsa.lookup_jar_by_type::<$fn_name>())
|
||||
}
|
||||
.get_or_init(|| *<dyn $Db as $Db>::zalsa_register_downcaster(db))
|
||||
}
|
||||
|
||||
pub fn fn_ingredient_mut(db: &mut dyn $Db) -> &mut $zalsa::function::IngredientImpl<Self> {
|
||||
|
|
159
src/database.rs
159
src/database.rs
|
@ -152,72 +152,41 @@ pub fn current_revision<Db: ?Sized + Database>(db: &Db) -> Revision {
|
|||
db.zalsa().current_revision()
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub use persistence::SerializeBuilder;
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub(crate) mod persistence {
|
||||
use crate::plumbing::{Ingredient, Persistable};
|
||||
use crate::zalsa::{HasJar, Zalsa};
|
||||
use crate::{Database, IngredientIndex};
|
||||
use crate::plumbing::Ingredient;
|
||||
use crate::zalsa::Zalsa;
|
||||
use crate::{Database, HasJar};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use serde::de::{self, DeserializeSeed, SeqAccess};
|
||||
use serde::de::DeserializeSeed;
|
||||
use serde::Deserializer;
|
||||
|
||||
impl dyn Database {
|
||||
/// Returns a type implementing [`serde::Serialize`], that can be used to serialize the
|
||||
/// current state of the database.
|
||||
pub fn as_serialize<'db, I>(
|
||||
&'db mut self,
|
||||
ingredients: impl Fn(SerializeBuilder<'db>) -> I,
|
||||
) -> impl serde::Serialize + '_
|
||||
where
|
||||
I: serde::Serialize + 'db,
|
||||
{
|
||||
(self.zalsa().runtime(), ingredients(SerializeBuilder(self)))
|
||||
pub fn as_serialize<'db>(&self) -> impl serde::Serialize + '_ {
|
||||
self.zalsa().runtime()
|
||||
}
|
||||
|
||||
/// Deserialize the database using a [`serde::Deserializer`].
|
||||
///
|
||||
/// This method will modify the database in-place based on the serialized data.
|
||||
pub fn deserialize<'db, D>(&mut self, deserializer: D) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'db>,
|
||||
{
|
||||
DeserializeDatabase(self.zalsa_mut()).deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ingredient_set! { Interned<'_>, Tracked<'_>, query, ... }
|
||||
// TODO: macro_rules! ingredient_set
|
||||
|
||||
pub struct SerializeBuilder<'db>(&'db dyn Database);
|
||||
|
||||
impl<'db> SerializeBuilder<'db> {
|
||||
pub fn ingredient<I>(&self) -> impl serde::Serialize + 'db
|
||||
where
|
||||
I: HasJar,
|
||||
I::Ingredient: Persistable,
|
||||
{
|
||||
// SAFETY: `Database::as_serialize` captures a mutable reference to the database.
|
||||
unsafe { I::ingredient(self.0).as_serialize(self.0.zalsa()) }
|
||||
/// Returns a type implementing [`DeserializeSeed`] that can be used to deserialize
|
||||
/// the database in-place.
|
||||
pub fn as_deserialize(&mut self) -> impl for<'de> DeserializeSeed<'de> + '_ {
|
||||
DeserializeDatabase(self.zalsa_mut())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeserializeBuilder<'db> {
|
||||
db: &'db dyn Database,
|
||||
ingredients: Vec<fn()>
|
||||
}
|
||||
struct DeserializeDatabase<'db>(&'db mut Zalsa);
|
||||
|
||||
impl<'db> SerializeBuilder<'db> {
|
||||
pub fn ingredient<I>(&self) -> impl serde::Serialize + 'db
|
||||
impl<'de> DeserializeSeed<'de> for DeserializeDatabase<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
I: HasJar,
|
||||
I::Ingredient: Persistable,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// SAFETY: `Database::as_serialize` captures a mutable reference to the database.
|
||||
unsafe { I::ingredient(self.0).as_serialize(self.0.zalsa()) }
|
||||
let mut runtime = <crate::Runtime as serde::Deserialize>::deserialize(deserializer)?;
|
||||
self.0.runtime_mut().deserialize_from(&mut runtime);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,97 +195,25 @@ pub(crate) mod persistence {
|
|||
///
|
||||
/// This method will temporarily remove the ingredient from the database, so any attempts
|
||||
/// to lookup the ingredient will fail.
|
||||
pub(crate) fn with_mut_ingredient<I, R>(
|
||||
ingredient: &I,
|
||||
zalsa: &mut Zalsa,
|
||||
f: impl FnOnce(&mut I, &mut Zalsa) -> R,
|
||||
pub fn with_mut_ingredient<I, R>(
|
||||
db: &mut dyn crate::Database,
|
||||
f: impl FnOnce(&mut I::Ingredient, &mut dyn crate::Database) -> R,
|
||||
) -> R
|
||||
where
|
||||
I: Ingredient,
|
||||
I: HasJar,
|
||||
{
|
||||
let index = ingredient.ingredient_index();
|
||||
let index = I::ingredient(db).ingredient_index();
|
||||
|
||||
// Remove the ingredient temporarily, to avoid holding overlapping mutable borrows.
|
||||
let mut ingredient = zalsa.take_ingredient(index);
|
||||
let mut ingredient = db.zalsa_mut().take_ingredient(index);
|
||||
|
||||
// Call the function with the concrete ingredient.
|
||||
let ingredient_mut = <dyn std::any::Any>::downcast_mut(&mut *ingredient).unwrap();
|
||||
let value = f(ingredient_mut, zalsa);
|
||||
let value = f(ingredient_mut, db);
|
||||
|
||||
zalsa.replace_ingredient(index, ingredient);
|
||||
db.zalsa_mut().replace_ingredient(index, ingredient);
|
||||
value
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(field_identifier, rename_all = "lowercase")]
|
||||
enum DatabaseField {
|
||||
Runtime,
|
||||
Ingredients,
|
||||
}
|
||||
|
||||
struct DeserializeDatabase<'db>(&'db mut Zalsa);
|
||||
|
||||
impl<'de> de::DeserializeSeed<'de> for DeserializeDatabase<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_seq(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for DeserializeDatabase<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("struct Database")
|
||||
}
|
||||
|
||||
fn visit_seq<V>(self, mut seq: V) -> Result<(), V::Error>
|
||||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let mut runtime = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
||||
let () = seq
|
||||
.next_element_seed(DeserializeIngredients(self.0))?
|
||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
||||
|
||||
self.0.runtime_mut().deserialize_from(&mut runtime);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DeserializeIngredients<'db>(&'db mut Zalsa);
|
||||
|
||||
impl<'de> serde::de::DeserializeSeed<'de> for DeserializeIngredients<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_seq(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for DeserializeIngredients<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
while let Some(x) = seq.next_element_seed(seed) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "salsa_unstable")]
|
||||
|
|
|
@ -264,31 +264,25 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub unsafe fn as_serialize<'db, S>(
|
||||
pub fn as_serialize<'db>(
|
||||
&'db self,
|
||||
zalsa: &'db Zalsa,
|
||||
db: &'db dyn crate::Database,
|
||||
) -> impl serde::Serialize + 'db {
|
||||
persistence::SerializeIngredient {
|
||||
zalsa,
|
||||
ingredient: self,
|
||||
zalsa: db.zalsa(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub fn deserialize<'de, D>(
|
||||
&mut self,
|
||||
zalsa: &mut Zalsa,
|
||||
deserializer: D,
|
||||
) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let deserialize = persistence::DeserializeIngredient {
|
||||
zalsa,
|
||||
pub fn as_deserialize<'db>(
|
||||
&'db mut self,
|
||||
db: &'db mut dyn crate::Database,
|
||||
) -> impl for<'de> serde::de::DeserializeSeed<'de, Value = ()> + 'db {
|
||||
persistence::DeserializeIngredient {
|
||||
ingredient: self,
|
||||
};
|
||||
|
||||
serde::de::DeserializeSeed::deserialize(deserialize, deserializer)
|
||||
zalsa: db.zalsa_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -218,53 +218,6 @@ pub trait Ingredient: Any + std::fmt::Debug + Send + Sync {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub trait Persistable: Ingredient {
|
||||
/// Returns a type implementing [`serde::Serialize`], that can be used to serialize the
|
||||
/// current state of the ingredient.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// While this method takes an immutable reference to the database, it can only be called when a
|
||||
/// the serializer has exclusive access to the database.
|
||||
unsafe fn as_serialize<'db>(&self, zalsa: &'db Zalsa) -> impl serde::Serialize + 'db;
|
||||
|
||||
/// Deserialize the ingredient using a [`serde::Deserializer`].
|
||||
///
|
||||
/// This method will modify the database in-place based on the serialized data.
|
||||
fn deserialize<'de, D>(&self, zalsa: &mut Zalsa, deserializer: D) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>;
|
||||
}
|
||||
|
||||
trait X: Any {
|
||||
fn bar(&self) -> usize;
|
||||
|
||||
fn foo(&self, x: &dyn Any) -> usize
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
x.downcast_ref::<Self>().unwrap().bar()
|
||||
}
|
||||
}
|
||||
|
||||
trait Y: Any {
|
||||
fn bar(&self) -> usize;
|
||||
fn foo(&self, y: &dyn Any) -> usize;
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl Y for Bar {
|
||||
fn bar(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn foo(&self, y: &dyn Any) -> usize {
|
||||
y.downcast_ref::<Self>().unwrap().bar()
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn Ingredient {
|
||||
/// Equivalent to the `downcast` method on `Any`.
|
||||
///
|
||||
|
|
26
src/input.rs
26
src/input.rs
|
@ -257,31 +257,25 @@ impl<C: Configuration> IngredientImpl<C> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub unsafe fn as_serialize<'db, S>(
|
||||
pub fn as_serialize<'db>(
|
||||
&'db self,
|
||||
zalsa: &'db Zalsa,
|
||||
db: &'db dyn crate::Database,
|
||||
) -> impl serde::Serialize + 'db {
|
||||
persistence::SerializeIngredient {
|
||||
zalsa,
|
||||
_ingredient: self,
|
||||
zalsa: db.zalsa(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub fn deserialize<'de, D>(
|
||||
&mut self,
|
||||
zalsa: &mut Zalsa,
|
||||
deserializer: D,
|
||||
) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let deserialize = persistence::DeserializeIngredient {
|
||||
zalsa,
|
||||
pub fn as_deserialize<'db>(
|
||||
&'db mut self,
|
||||
db: &'db mut dyn crate::Database,
|
||||
) -> impl for<'de> serde::de::DeserializeSeed<'de, Value = ()> + 'db {
|
||||
persistence::DeserializeIngredient {
|
||||
ingredient: self,
|
||||
};
|
||||
|
||||
serde::de::DeserializeSeed::deserialize(deserialize, deserializer)
|
||||
zalsa: db.zalsa_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -838,25 +838,25 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub unsafe fn as_serialize<'db, S>(
|
||||
pub fn as_serialize<'db>(
|
||||
&'db self,
|
||||
zalsa: &'db Zalsa,
|
||||
db: &'db dyn crate::Database,
|
||||
) -> impl serde::Serialize + 'db {
|
||||
persistence::SerializeIngredient {
|
||||
zalsa,
|
||||
ingredient: self,
|
||||
zalsa: db.zalsa(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub fn deserialize<'de, D>(&self, zalsa: &mut Zalsa, deserializer: D) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
crate::database::persistence::with_mut_ingredient(self, zalsa, |ingredient, zalsa| {
|
||||
let deserialize = persistence::DeserializeIngredient { zalsa, ingredient };
|
||||
serde::de::DeserializeSeed::deserialize(deserialize, deserializer)
|
||||
})
|
||||
pub fn as_deserialize<'db>(
|
||||
&'db mut self,
|
||||
db: &'db mut dyn crate::Database,
|
||||
) -> impl for<'de> serde::de::DeserializeSeed<'de, Value = ()> + 'db {
|
||||
persistence::DeserializeIngredient {
|
||||
ingredient: self,
|
||||
zalsa: db.zalsa_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,11 @@ pub use self::revision::Revision;
|
|||
pub use self::runtime::Runtime;
|
||||
pub use self::storage::{Storage, StorageHandle};
|
||||
pub use self::update::Update;
|
||||
pub use self::zalsa::IngredientIndex;
|
||||
pub use self::zalsa::{HasJar, IngredientIndex};
|
||||
pub use crate::attach::{attach, with_attached_database};
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub use database::SerializeBuilder;
|
||||
pub use self::database::persistence::with_mut_ingredient;
|
||||
|
||||
pub mod prelude {
|
||||
#[cfg(feature = "accumulator")]
|
||||
|
@ -104,7 +104,7 @@ pub mod plumbing {
|
|||
pub use crate::database::{current_revision, Database};
|
||||
pub use crate::durability::Durability;
|
||||
pub use crate::id::{AsId, FromId, FromIdWithDb, Id};
|
||||
pub use crate::ingredient::{Ingredient, Jar, Location, Persistable};
|
||||
pub use crate::ingredient::{Ingredient, Jar, Location};
|
||||
pub use crate::ingredient_cache::IngredientCache;
|
||||
pub use crate::key::DatabaseKeyIndex;
|
||||
pub use crate::memo_ingredient_indices::{
|
||||
|
|
|
@ -912,28 +912,25 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub unsafe fn as_serialize<'db, S>(&'db self, zalsa: &'db Zalsa) -> impl serde::Serialize + 'db {
|
||||
pub fn as_serialize<'db>(
|
||||
&'db self,
|
||||
db: &'db dyn crate::Database,
|
||||
) -> impl serde::Serialize + 'db {
|
||||
persistence::SerializeIngredient {
|
||||
zalsa,
|
||||
_ingredient: self,
|
||||
zalsa: db.zalsa(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistence")]
|
||||
pub fn deserialize<'de, D>(
|
||||
&mut self,
|
||||
zalsa: &mut Zalsa,
|
||||
deserializer: D,
|
||||
) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let deserialize = persistence::DeserializeIngredient {
|
||||
zalsa,
|
||||
pub fn as_deserialize<'db>(
|
||||
&'db mut self,
|
||||
db: &'db mut dyn crate::Database,
|
||||
) -> impl for<'de> serde::de::DeserializeSeed<'de, Value = ()> + 'db {
|
||||
persistence::DeserializeIngredient {
|
||||
ingredient: self,
|
||||
};
|
||||
|
||||
serde::de::DeserializeSeed::deserialize(deserialize, deserializer)
|
||||
zalsa: db.zalsa_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,87 @@
|
|||
mod common;
|
||||
|
||||
use common::LogDatabase;
|
||||
use salsa::{Database, Durability, Setter};
|
||||
use salsa::{Database, Durability, HasJar, Setter};
|
||||
|
||||
use expect_test::expect;
|
||||
|
||||
use serde::ser::SerializeTupleStruct;
|
||||
|
||||
struct SerializeDatabase<'db>(&'db dyn salsa::Database);
|
||||
|
||||
impl serde::Serialize for SerializeDatabase<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let SerializeDatabase(db) = *self;
|
||||
|
||||
let mut seq = serializer.serialize_tuple_struct("Database", 14)?;
|
||||
|
||||
seq.serialize_field(&db.as_serialize());
|
||||
|
||||
// TODO: `seq.serialize_field(salsa::serialize_ingredient::<MyInput>())`
|
||||
|
||||
seq.serialize_field(&MyInput::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&MySingleton::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&MyInterned::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&MyTracked::ingredient(db).as_serialize(db))?;
|
||||
|
||||
seq.serialize_field(&unit_to_interned::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&input_to_tracked::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&input_pair_to_string::ingredient(db).as_serialize(db))?;
|
||||
|
||||
seq.serialize_field(&partial_query::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&partial_query_inner::ingredient(db).as_serialize(db))?;
|
||||
|
||||
seq.serialize_field(&partial_query_intern::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&partial_query_intern_inner::ingredient(db).as_serialize(db))?;
|
||||
|
||||
seq.serialize_field(&specify::ingredient(db).as_serialize(db))?;
|
||||
seq.serialize_field(&specified_query::ingredient(db).as_serialize(db))?;
|
||||
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct DeserializeDatabase<'db>(&'db mut dyn salsa::Database);
|
||||
|
||||
impl<'de> serde::de::DeserializeSeed<'de> for DeserializeDatabase<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_tuple_struct("Database", 14, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for DeserializeDatabase<'_> {
|
||||
type Value = ();
|
||||
|
||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "a sequence")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let DeserializeDatabase(db) = self;
|
||||
|
||||
seq.next_element_seed(db.as_deserialize());
|
||||
|
||||
// TODO: `salsa::deserialize_ingredient::<MyInput>(db, |seed| seq.next_element_seed(seed))`
|
||||
|
||||
salsa::with_mut_ingredient::<MyInput, _>(db, |i, db| {
|
||||
seq.next_element_seed(i.as_deserialize(db))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::input(persist)]
|
||||
struct MyInput {
|
||||
field: usize,
|
||||
|
@ -49,8 +126,7 @@ fn everything() {
|
|||
let _input1 = MyInput::new(&db, 1);
|
||||
let _input2 = MyInput::new(&db, 2);
|
||||
|
||||
let serialized =
|
||||
serde_json::to_string_pretty(&<dyn salsa::Database>::as_serialize(&mut db)).unwrap();
|
||||
let serialized = serde_json::to_string_pretty(&SerializeDatabase(&db)).unwrap();
|
||||
|
||||
let expected = expect![[r#"
|
||||
{
|
||||
|
@ -99,8 +175,7 @@ fn everything() {
|
|||
let _out = input_to_tracked(&db, input1);
|
||||
let _out = input_pair_to_string(&db, input1, input2);
|
||||
|
||||
let serialized =
|
||||
serde_json::to_string_pretty(&<dyn salsa::Database>::as_serialize(&mut db)).unwrap();
|
||||
let serialized = serde_json::to_string_pretty(&SerializeDatabase(&db)).unwrap();
|
||||
|
||||
let expected = expect![[r#"
|
||||
{
|
||||
|
@ -312,30 +387,29 @@ fn everything() {
|
|||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_query() {
|
||||
use salsa::plumbing::{FromId, ZalsaDatabase};
|
||||
|
||||
#[salsa::tracked(persist)]
|
||||
fn query<'db>(db: &'db dyn salsa::Database, input: MyInput) -> usize {
|
||||
inner_query(db, input) + 1
|
||||
}
|
||||
|
||||
#[salsa::tracked(persist)]
|
||||
fn partial_query<'db>(db: &'db dyn salsa::Database, input: MyInput) -> usize {
|
||||
// Note that the inner query is not persisted, but we should still preserve the dependency on `input.field`.
|
||||
#[salsa::tracked]
|
||||
fn inner_query<'db>(db: &'db dyn salsa::Database, input: MyInput) -> usize {
|
||||
input.field(db)
|
||||
}
|
||||
partial_query_inner(db, input) + 1
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn partial_query_inner<'db>(db: &'db dyn salsa::Database, input: MyInput) -> usize {
|
||||
input.field(db)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_query() {
|
||||
use salsa::plumbing::{FromId, ZalsaDatabase};
|
||||
|
||||
let mut db = common::EventLoggerDatabase::default();
|
||||
|
||||
let input = MyInput::new(&db, 0);
|
||||
|
||||
let result = query(&db, input);
|
||||
let result = partial_query(&db, input);
|
||||
assert_eq!(result, 1);
|
||||
|
||||
let serialized =
|
||||
serde_json::to_string_pretty(&<dyn salsa::Database>::as_serialize(&mut db)).unwrap();
|
||||
let serialized = serde_json::to_string_pretty(&SerializeDatabase(&db)).unwrap();
|
||||
let expected = expect![[r#"
|
||||
{
|
||||
"runtime": {
|
||||
|
@ -397,7 +471,7 @@ fn partial_query() {
|
|||
.expect("`MyInput` was persisted");
|
||||
let input = MyInput::from_id(id.key_index());
|
||||
|
||||
let result = query(&db, input);
|
||||
let result = partial_query(&db, input);
|
||||
assert_eq!(result, 1);
|
||||
|
||||
// The query was not re-executed.
|
||||
|
@ -409,7 +483,7 @@ fn partial_query() {
|
|||
|
||||
input.set_field(&mut db).to(1);
|
||||
|
||||
let result = query(&db, input);
|
||||
let result = partial_query(&db, input);
|
||||
assert_eq!(result, 2);
|
||||
|
||||
// The query was re-executed afer the input was updated.
|
||||
|
@ -423,35 +497,38 @@ fn partial_query() {
|
|||
]"#]]);
|
||||
}
|
||||
|
||||
#[salsa::tracked(persist)]
|
||||
fn partial_query_intern<'db>(
|
||||
db: &'db dyn salsa::Database,
|
||||
input: MyInput,
|
||||
value: usize,
|
||||
) -> MyInterned<'db> {
|
||||
partial_query_intern_inner(db, input, value)
|
||||
}
|
||||
|
||||
// Note that the inner query is not persisted, but we should still preserve the dependency on `MyInterned`.
|
||||
#[salsa::tracked]
|
||||
fn partial_query_intern_inner<'db>(
|
||||
db: &'db dyn salsa::Database,
|
||||
input: MyInput,
|
||||
value: usize,
|
||||
) -> MyInterned<'db> {
|
||||
let _i = input.field(db); // Only low durability interned values are garbage collected.
|
||||
MyInterned::new(db, value.to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_query_interned() {
|
||||
use salsa::plumbing::{AsId, FromId, ZalsaDatabase};
|
||||
|
||||
#[salsa::tracked(persist)]
|
||||
fn intern<'db>(db: &'db dyn salsa::Database, input: MyInput, value: usize) -> MyInterned<'db> {
|
||||
do_intern(db, input, value)
|
||||
}
|
||||
|
||||
// Note that the inner query is not persisted, but we should still preserve the dependency on `MyInterned`.
|
||||
#[salsa::tracked]
|
||||
fn do_intern<'db>(
|
||||
db: &'db dyn salsa::Database,
|
||||
input: MyInput,
|
||||
value: usize,
|
||||
) -> MyInterned<'db> {
|
||||
let _i = input.field(db); // Only low durability interned values are garbage collected.
|
||||
MyInterned::new(db, value.to_string())
|
||||
}
|
||||
|
||||
let mut db = common::EventLoggerDatabase::default();
|
||||
let input = MyInput::builder(0).durability(Durability::LOW).new(&db);
|
||||
|
||||
// Intern `i0`.
|
||||
let i0 = intern(&db, input, 0);
|
||||
let i0 = partial_query_intern(&db, input, 0);
|
||||
assert_eq!(i0.field(&db), "0");
|
||||
|
||||
let serialized =
|
||||
serde_json::to_string_pretty(&<dyn salsa::Database>::as_serialize(&mut db)).unwrap();
|
||||
let serialized = serde_json::to_string_pretty(&SerializeDatabase(&db)).unwrap();
|
||||
let expected = expect![[r#"
|
||||
{
|
||||
"runtime": {
|
||||
|
@ -537,7 +614,7 @@ fn partial_query_interned() {
|
|||
let input = MyInput::from_id(id.key_index());
|
||||
|
||||
// Re-intern `i0`.
|
||||
let i0 = intern(&db, input, 0);
|
||||
let i0 = partial_query_intern(&db, input, 0);
|
||||
let i0_id = i0.as_id();
|
||||
assert_eq!(i0.field(&db), "0");
|
||||
|
||||
|
@ -552,7 +629,7 @@ fn partial_query_interned() {
|
|||
for x in 1.. {
|
||||
db.synthetic_write(Durability::LOW);
|
||||
|
||||
let ix = intern(&db, input, x);
|
||||
let ix = partial_query_intern(&db, input, x);
|
||||
let ix_id = ix.as_id();
|
||||
|
||||
// We reused the slot of `i0`.
|
||||
|
@ -562,7 +639,7 @@ fn partial_query_interned() {
|
|||
}
|
||||
|
||||
// Re-intern `i0` after is has been garbage collected.
|
||||
let i0 = intern(&db, input, 0);
|
||||
let i0 = partial_query_intern(&db, input, 0);
|
||||
|
||||
// The query was re-executed due to garbage collection, even though no inputs have changed
|
||||
// and the inner query was not persisted.
|
||||
|
@ -570,47 +647,23 @@ fn partial_query_interned() {
|
|||
assert_ne!(i0_id.index(), i0.as_id().index());
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn specify<'db>(db: &'db dyn salsa::Database) {
|
||||
let tracked = MyTracked::new(db, "a".to_string());
|
||||
specified_query::specify(db, tracked, 2222);
|
||||
}
|
||||
|
||||
#[salsa::tracked(specify, persist)]
|
||||
fn specified_query<'db>(_db: &'db dyn salsa::Database, _tracked: MyTracked<'db>) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "must be persistable")]
|
||||
fn invalid_specified_dependency() {
|
||||
#[salsa::tracked]
|
||||
fn specify<'db>(db: &'db dyn salsa::Database) {
|
||||
let tracked = MyTracked::new(db, "a".to_string());
|
||||
specified_query::specify(db, tracked, 2222);
|
||||
}
|
||||
|
||||
#[salsa::tracked(specify, persist)]
|
||||
fn specified_query<'db>(_db: &'db dyn salsa::Database, _tracked: MyTracked<'db>) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
let mut db = common::LoggerDatabase::default();
|
||||
let db = common::LoggerDatabase::default();
|
||||
|
||||
specify(&db);
|
||||
|
||||
let _serialized =
|
||||
serde_json::to_string_pretty(&<dyn salsa::Database>::as_serialize(&mut db)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_nothing() {
|
||||
let mut db = common::LoggerDatabase::default();
|
||||
|
||||
let serialized =
|
||||
serde_json::to_string_pretty(&<dyn salsa::Database>::as_serialize(&mut db)).unwrap();
|
||||
|
||||
// Empty ingredients should not be serialized.
|
||||
let expected = expect![[r#"
|
||||
{
|
||||
"runtime": {
|
||||
"revisions": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"ingredients": {}
|
||||
}"#]];
|
||||
|
||||
expected.assert_eq(&serialized);
|
||||
let _serialized = serde_json::to_string_pretty(&SerializeDatabase(&db)).unwrap();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue