mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-13 00:40:22 +00:00
Add lazy-input (on-demand) example
This commit is contained in:
parent
5b8464c4f9
commit
609acc396c
7 changed files with 165 additions and 0 deletions
|
@ -37,4 +37,5 @@ members = [
|
|||
"components/salsa-2022-macros",
|
||||
"calc-example/calc",
|
||||
"salsa-2022-tests",
|
||||
"lazy-input",
|
||||
]
|
||||
|
|
14
lazy-input/Cargo.toml
Normal file
14
lazy-input/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "lazy-input"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
salsa = { path = "../components/salsa-2022", package = "salsa-2022" }
|
||||
ordered-float = "3.0"
|
||||
dashmap = "5.4.0"
|
||||
notify = "5.0.0"
|
||||
crossbeam-channel = "0.5.6"
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
2
lazy-input/inputs/a
Normal file
2
lazy-input/inputs/a
Normal file
|
@ -0,0 +1,2 @@
|
|||
2
|
||||
./aa
|
1
lazy-input/inputs/aa
Normal file
1
lazy-input/inputs/aa
Normal file
|
@ -0,0 +1 @@
|
|||
8
|
1
lazy-input/inputs/b
Normal file
1
lazy-input/inputs/b
Normal file
|
@ -0,0 +1 @@
|
|||
4
|
3
lazy-input/inputs/start
Normal file
3
lazy-input/inputs/start
Normal file
|
@ -0,0 +1,3 @@
|
|||
1
|
||||
./a
|
||||
./b
|
143
lazy-input/src/main.rs
Normal file
143
lazy-input/src/main.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use std::{path::PathBuf, sync::Mutex};
|
||||
|
||||
use crossbeam_channel::{unbounded, Sender};
|
||||
use dashmap::DashMap;
|
||||
use notify::{
|
||||
event::ModifyKind, recommended_watcher, EventKind, RecommendedWatcher, RecursiveMode, Watcher,
|
||||
};
|
||||
use salsa::DebugWithDb;
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = unbounded();
|
||||
let mut db = Database::new(tx);
|
||||
let initial = db.input("./lazy-input/inputs/start".parse().unwrap());
|
||||
loop {
|
||||
let parsed = parse(&db, initial);
|
||||
let sum = sum(&db, parsed);
|
||||
|
||||
println!("{}", sum);
|
||||
for log in db.logs.lock().unwrap().drain(..) {
|
||||
println!("{}", log);
|
||||
}
|
||||
|
||||
loop {
|
||||
let event = rx.recv().unwrap().unwrap();
|
||||
let paths: Vec<_> = if event.need_rescan() {
|
||||
db.files.iter().map(|entry| entry.key().clone()).collect()
|
||||
} else if matches!(
|
||||
event.kind,
|
||||
EventKind::Access(_) | EventKind::Modify(ModifyKind::Metadata(_))
|
||||
) {
|
||||
continue;
|
||||
} else {
|
||||
event.paths
|
||||
};
|
||||
if paths.is_empty() {
|
||||
continue;
|
||||
}
|
||||
for path in paths {
|
||||
let path = path.canonicalize().unwrap();
|
||||
let file = match db.files.get(&path) {
|
||||
Some(file) => *file,
|
||||
None => continue,
|
||||
};
|
||||
file.set_contents(&mut db)
|
||||
.to(std::fs::read_to_string(path).unwrap());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::jar(db = Db)]
|
||||
struct Jar(File, ParsedFile, parse, sum);
|
||||
|
||||
trait Db: salsa::DbWithJar<Jar> {
|
||||
fn input(&self, path: PathBuf) -> File;
|
||||
}
|
||||
|
||||
#[salsa::db(Jar)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
logs: Mutex<Vec<String>>,
|
||||
files: DashMap<PathBuf, File>,
|
||||
file_watcher: Mutex<RecommendedWatcher>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
fn new(tx: Sender<notify::Result<notify::Event>>) -> Self {
|
||||
let storage = Default::default();
|
||||
Self {
|
||||
storage,
|
||||
logs: Default::default(),
|
||||
files: DashMap::new(),
|
||||
file_watcher: Mutex::new(recommended_watcher(tx).unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl salsa::Database for Database {
|
||||
fn salsa_event(&self, event: salsa::Event) {
|
||||
// don't log boring events
|
||||
if let salsa::EventKind::WillExecute { .. } = event.kind {
|
||||
self.logs
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(format!("Event: {:?}", event.debug(self)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Db for Database {
|
||||
fn input(&self, path: PathBuf) -> File {
|
||||
let path = path.canonicalize().unwrap();
|
||||
*self.files.entry(path.clone()).or_insert_with(|| {
|
||||
let watcher = &mut *self.file_watcher.lock().unwrap();
|
||||
watcher.watch(&path, RecursiveMode::NonRecursive).unwrap();
|
||||
let contents = std::fs::read_to_string(&path).unwrap();
|
||||
File::new(self, path, contents)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::input]
|
||||
struct File {
|
||||
path: PathBuf,
|
||||
#[return_ref]
|
||||
contents: String,
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn parse(db: &dyn Db, input: File) -> ParsedFile {
|
||||
let mut lines = input.contents(db).lines();
|
||||
let value = lines.next().unwrap().parse().unwrap();
|
||||
let links = lines
|
||||
.map(|path| {
|
||||
let link_path = input
|
||||
.path(db)
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(path.parse::<PathBuf>().unwrap());
|
||||
let file = db.input(link_path);
|
||||
parse(db, file)
|
||||
})
|
||||
.collect();
|
||||
ParsedFile::new(db, value, links)
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
struct ParsedFile {
|
||||
value: u32,
|
||||
#[return_ref]
|
||||
links: Vec<ParsedFile>,
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn sum(db: &dyn Db, input: ParsedFile) -> u32 {
|
||||
input.value(db)
|
||||
+ input
|
||||
.links(db)
|
||||
.iter()
|
||||
.map(|&file| sum(db, file))
|
||||
.sum::<u32>()
|
||||
}
|
Loading…
Reference in a new issue