-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathcalc.rs
67 lines (54 loc) · 2.05 KB
/
calc.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! This example demonstrates how to memoize the execution of scripts which can
//! depend on other scripts---invalidating the result of a script's execution
//! only if a file it depends on changes.
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use comemo::{memoize, track, Track, Tracked};
fn main() {
// Create some scripts in the calc language. This language supports addition
// and `eval` statements referring to other files.
let mut files = Files(HashMap::new());
files.write("alpha.calc", "2 + eval beta.calc");
files.write("beta.calc", "2 + 3");
files.write("gamma.calc", "8 + 3");
// [Miss] The cache is empty.
assert_eq!(evaluate("eval alpha.calc", files.track()), 7);
// [Miss] This is not a top-level hit because this exact string was never
// passed to `evaluate`, but this does not compute "2 + 3" again.
assert_eq!(evaluate("eval beta.calc", files.track()), 5);
// Modify the gamma file.
files.write("gamma.calc", "42");
// [Hit] This is a hit because `gamma.calc` isn't referenced by `alpha.calc`.
assert_eq!(evaluate("eval alpha.calc", files.track()), 7);
// Modify the beta file.
files.write("beta.calc", "4 + eval gamma.calc");
// [Miss] This is a miss because `beta.calc` changed.
assert_eq!(evaluate("eval alpha.calc", files.track()), 48);
}
/// Evaluate a `.calc` script.
#[memoize]
fn evaluate(script: &str, files: Tracked<Files>) -> i32 {
script
.split('+')
.map(str::trim)
.map(|part| match part.strip_prefix("eval ") {
Some(path) => evaluate(&files.read(path), files),
None => part.parse::<i32>().unwrap(),
})
.sum()
}
/// File storage.
struct Files(HashMap<PathBuf, String>);
#[track]
impl Files {
/// Read a file from storage.
fn read(&self, path: &str) -> String {
self.0.get(Path::new(path)).cloned().unwrap_or_default()
}
}
impl Files {
/// Write a file to storage.
fn write(&mut self, path: &str, text: &str) {
self.0.insert(path.into(), text.into());
}
}