mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-02 09:46:06 +00:00
Merge branch 'salsa-rs:master' into master
This commit is contained in:
commit
5d1ff35f6d
64 changed files with 236 additions and 55 deletions
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
|
@ -33,6 +33,10 @@ jobs:
|
|||
toolchain: ${{ matrix.rust }}
|
||||
components: rustfmt, clippy
|
||||
default: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
|
|
|
@ -33,8 +33,8 @@ insta = "1.8.0"
|
|||
[workspace]
|
||||
members = [
|
||||
"components/salsa-macros",
|
||||
"components/salsa-entity-mock",
|
||||
"components/salsa-entity-macros",
|
||||
"components/salsa-2022",
|
||||
"components/salsa-2022-macros",
|
||||
"calc-example/calc",
|
||||
"salsa-2022-tests"
|
||||
]
|
|
@ -1,3 +1,3 @@
|
|||
> ⚠️ **IN-PROGRESS VERSION OF SALSA.** ⚠️
|
||||
>
|
||||
> This page describes the unreleased "Salsa 2022" version, which is a major departure from older versions of salsa. The code here works but is only available on github and from the `salsa-entity-mock` crate.
|
||||
> This page describes the unreleased "Salsa 2022" version, which is a major departure from older versions of salsa. The code here works but is only available on github and from the `salsa-2022` crate.
|
||||
|
|
|
@ -33,7 +33,7 @@ In addition to the struct itself, we must add an impl of `salsa::Database`:
|
|||
If you want to permit accessing your database from multiple threads at once, then you also need to implement the `ParallelDatabase` trait:
|
||||
|
||||
```rust
|
||||
{{#include ../../../calc-example/calc/src/db.rs:db_impl}}
|
||||
{{#include ../../../calc-example/calc/src/db.rs:par_db_impl}}
|
||||
```
|
||||
|
||||
## Implementing the `Default` trait
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
salsa = { path = "../../components/salsa-entity-mock", package = "salsa-entity-mock" }
|
||||
salsa = { path = "../../components/salsa-2022", package = "salsa-2022" }
|
||||
ordered-float = "3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "salsa-entity-macros"
|
||||
name = "salsa-2022-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
|
@ -345,11 +345,3 @@ impl SalsaField {
|
|||
!self.has_no_eq_attr
|
||||
}
|
||||
}
|
||||
|
||||
/// True if this an attribute that salsa permits users to attach to
|
||||
/// entity/interned fields.
|
||||
fn is_entity_like_field_attribute(a: &syn::Attribute) -> bool {
|
||||
FIELD_OPTION_ATTRIBUTES
|
||||
.iter()
|
||||
.any(|(fa, _)| a.path.is_ident(fa))
|
||||
}
|
|
@ -370,10 +370,9 @@ fn specify_fn(
|
|||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<Option<syn::ImplItemMethod>> {
|
||||
let specify = match &args.specify {
|
||||
Some(s) => s,
|
||||
None => return Ok(None),
|
||||
};
|
||||
if args.specify.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// `specify` has the same signature as the original,
|
||||
// but it takes a value arg and has no return type.
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "salsa-entity-mock"
|
||||
name = "salsa-2022"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -16,4 +16,4 @@ crossbeam-utils = { version = "0.8", default-features = false }
|
|||
log = "0.4.5"
|
||||
parking_lot = "0.11.0"
|
||||
smallvec = "1.0.0"
|
||||
salsa-entity-macros = { path = "../salsa-entity-macros" }
|
||||
salsa-2022-macros = { path = "../salsa-2022-macros" }
|
|
@ -67,7 +67,7 @@ pub trait ParallelDatabase: Database + Send {
|
|||
/// fn snapshot(&self) -> Snapshot<Self> {
|
||||
/// Snapshot::new(
|
||||
/// MyDatabaseType {
|
||||
/// runtime: self.runtime.snapshot(self),
|
||||
/// runtime: self.storage.snapshot(),
|
||||
/// other_field: self.other_field.clone(),
|
||||
/// }
|
||||
/// )
|
|
@ -41,9 +41,9 @@ pub use self::storage::DbWithJar;
|
|||
pub use self::storage::Storage;
|
||||
pub use self::tracked_struct::TrackedStructData;
|
||||
pub use self::tracked_struct::TrackedStructId;
|
||||
pub use salsa_entity_macros::accumulator;
|
||||
pub use salsa_entity_macros::db;
|
||||
pub use salsa_entity_macros::input;
|
||||
pub use salsa_entity_macros::interned;
|
||||
pub use salsa_entity_macros::jar;
|
||||
pub use salsa_entity_macros::tracked;
|
||||
pub use salsa_2022_macros::accumulator;
|
||||
pub use salsa_2022_macros::db;
|
||||
pub use salsa_2022_macros::input;
|
||||
pub use salsa_2022_macros::interned;
|
||||
pub use salsa_2022_macros::jar;
|
||||
pub use salsa_2022_macros::tracked;
|
|
@ -6,11 +6,6 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
salsa = { path = "../components/salsa-entity-mock", package = "salsa-entity-mock" }
|
||||
|
||||
[dev-dependencies]
|
||||
salsa = { path = "../components/salsa-2022", package = "salsa-2022" }
|
||||
expect-test = "1.4.0"
|
||||
|
||||
[[bin]]
|
||||
name = "salsa-2022-tests"
|
||||
path = "main.rs"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
//! This crate has the beginning of various unit tests on salsa 2022
|
||||
//! code.
|
||||
|
||||
mod tracked_fn_on_input;
|
||||
mod tracked_fn_on_tracked;
|
||||
mod tracked_fn_on_tracked_specify;
|
||||
|
||||
fn main() {}
|
24
salsa-2022-tests/src/lib.rs
Normal file
24
salsa-2022-tests/src/lib.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
/// Utility for tests that lets us log when notable events happen.
|
||||
#[derive(Default)]
|
||||
pub struct Logger {
|
||||
logs: std::sync::Mutex<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Trait implemented by databases that lets them log events.
|
||||
pub trait HasLogger {
|
||||
/// Return a reference to the logger from the database.
|
||||
fn logger(&self) -> &Logger;
|
||||
|
||||
/// Log an event from inside a tracked function.
|
||||
fn push_log(&self, string: String) {
|
||||
self.logger().logs.lock().unwrap().push(string);
|
||||
}
|
||||
|
||||
/// Asserts what the (formatted) logs should look like,
|
||||
/// clearing the logged events. This takes `&mut self` because
|
||||
/// it is meant to be run from outside any tracked functions.
|
||||
fn assert_logs(&mut self, expected: expect_test::Expect) {
|
||||
let logs = std::mem::replace(&mut *self.logger().logs.lock().unwrap(), vec![]);
|
||||
expected.assert_eq(&format!("{:#?}", logs));
|
||||
}
|
||||
}
|
85
salsa-2022-tests/tests/hello_world.rs
Normal file
85
salsa-2022-tests/tests/hello_world.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use salsa_2022_tests::{HasLogger, Logger};
|
||||
|
||||
use expect_test::expect;
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, MyTracked, final_result, intermediate_result);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
|
||||
|
||||
#[salsa::input(jar = Jar)]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
fn final_result(db: &dyn Db, input: MyInput) -> u32 {
|
||||
db.push_log(format!("final_result({:?})", input));
|
||||
intermediate_result(db, input).field(db) * 2
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
struct MyTracked {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked {
|
||||
db.push_log(format!("intermediate_result({:?})", input));
|
||||
MyTracked::new(db, input.field(db) / 2)
|
||||
}
|
||||
|
||||
#[salsa::db(Jar)]
|
||||
#[derive(Default)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
impl salsa::Database for Database {
|
||||
fn salsa_runtime(&self) -> &salsa::Runtime {
|
||||
self.storage.runtime()
|
||||
}
|
||||
}
|
||||
|
||||
impl Db for Database {}
|
||||
|
||||
impl HasLogger for Database {
|
||||
fn logger(&self) -> &Logger {
|
||||
&self.logger
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&mut db, 22);
|
||||
assert_eq!(final_result(&db, input), 22);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"final_result(MyInput(Id { value: 1 }))",
|
||||
"intermediate_result(MyInput(Id { value: 1 }))",
|
||||
]"#]]);
|
||||
|
||||
// Intermediate result is the same, so final result does
|
||||
// not need to be recomputed:
|
||||
input.set_field(&mut db, 23);
|
||||
assert_eq!(final_result(&db, input), 22);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"intermediate_result(MyInput(Id { value: 1 }))",
|
||||
]"#]]);
|
||||
|
||||
input.set_field(&mut db, 24);
|
||||
assert_eq!(final_result(&db, input), 24);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"intermediate_result(MyInput(Id { value: 1 }))",
|
||||
"final_result(MyInput(Id { value: 1 }))",
|
||||
]"#]]);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
#![allow(warnings)]
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, tracked_fn);
|
|
@ -1,5 +1,6 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, MyTracked, tracked_fn);
|
||||
|
@ -21,22 +22,22 @@ fn tracked_fn(db: &dyn Db, input: MyInput) -> MyTracked {
|
|||
MyTracked::new(db, input.field(db) * 2)
|
||||
}
|
||||
|
||||
#[salsa::db(Jar)]
|
||||
#[derive(Default)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
}
|
||||
|
||||
impl salsa::Database for Database {
|
||||
fn salsa_runtime(&self) -> &salsa::Runtime {
|
||||
self.storage.runtime()
|
||||
}
|
||||
}
|
||||
|
||||
impl Db for Database {}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
#[salsa::db(Jar)]
|
||||
#[derive(Default)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
}
|
||||
|
||||
impl salsa::Database for Database {
|
||||
fn salsa_runtime(&self) -> &salsa::Runtime {
|
||||
self.storage.runtime()
|
||||
}
|
||||
}
|
||||
|
||||
impl Db for Database {}
|
||||
|
||||
let mut db = Database::default();
|
||||
let input = MyInput::new(&mut db, 22);
|
||||
assert_eq!(tracked_fn(&db, input).field(&db), 44);
|
|
@ -1,5 +1,6 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
#![allow(warnings)]
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, MyTracked, tracked_fn, tracked_fn_extra);
|
87
salsa-2022-tests/tests/tracked_fn_read_own_entity.rs
Normal file
87
salsa-2022-tests/tests/tracked_fn_read_own_entity.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use salsa_2022_tests::{HasLogger, Logger};
|
||||
|
||||
use expect_test::expect;
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(MyInput, MyTracked, final_result, intermediate_result);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
|
||||
|
||||
#[salsa::input(jar = Jar)]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
fn final_result(db: &dyn Db, input: MyInput) -> u32 {
|
||||
db.push_log(format!("final_result({:?})", input));
|
||||
intermediate_result(db, input).field(db) * 2
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
struct MyTracked {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked(jar = Jar)]
|
||||
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked {
|
||||
db.push_log(format!("intermediate_result({:?})", input));
|
||||
let tracked = MyTracked::new(db, input.field(db) / 2);
|
||||
let _ = tracked.field(db); // read the field of an entity we created
|
||||
tracked
|
||||
}
|
||||
|
||||
#[salsa::db(Jar)]
|
||||
#[derive(Default)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
impl salsa::Database for Database {
|
||||
fn salsa_runtime(&self) -> &salsa::Runtime {
|
||||
self.storage.runtime()
|
||||
}
|
||||
}
|
||||
|
||||
impl Db for Database {}
|
||||
|
||||
impl HasLogger for Database {
|
||||
fn logger(&self) -> &Logger {
|
||||
&self.logger
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&mut db, 22);
|
||||
assert_eq!(final_result(&db, input), 22);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"final_result(MyInput(Id { value: 1 }))",
|
||||
"intermediate_result(MyInput(Id { value: 1 }))",
|
||||
]"#]]);
|
||||
|
||||
// Intermediate result is the same, so final result does
|
||||
// not need to be recomputed:
|
||||
input.set_field(&mut db, 23);
|
||||
assert_eq!(final_result(&db, input), 22);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"intermediate_result(MyInput(Id { value: 1 }))",
|
||||
]"#]]);
|
||||
|
||||
input.set_field(&mut db, 24);
|
||||
assert_eq!(final_result(&db, input), 24);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"intermediate_result(MyInput(Id { value: 1 }))",
|
||||
"final_result(MyInput(Id { value: 1 }))",
|
||||
]"#]]);
|
||||
}
|
Loading…
Reference in a new issue