aeac86ddd2
.base model (filters, formulas, properties, views; unknown keys kept in order; limits and invalid-value errors), evaluator with the D5 type rules, lazy if and boolean operators, memoised formulas with cycle errors, depth cap 200, string/number/object/list/date/link/file/regexp functions with lambdas (regex through notescraft-regex), and the map model (Web Mercator, tile ids, viewport tiles, grid clustering, coordinate and colour parsing, OSM attribution constants; Paris z10 is tile 518/352). Bases tasks 2, 5, 6 (Claude Sonnet) and 8 (MiniMax M3.1 Flash, OpenCode Zen free route). Verified: notescraft-bases suites all pass, clippy -D warnings, xtask layers and tree clean. Known gaps: toFixed rounds ties to even, list.unique is quadratic. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
108 lines
3.1 KiB
Rust
108 lines
3.1 KiB
Rust
//! Shared fixtures of the evaluator tests: a `Vec` of rows behind `NoteSource`.
|
|
#![allow(dead_code)] // shared test helpers: each test file uses only some of them
|
|
|
|
use notescraft_bases::{Env, Formulas, NoteSource, RowRef, Value, eval};
|
|
|
|
/// 2024-12-01 12:00:00 UTC.
|
|
pub const NOW: i64 = 1_733_054_400_000;
|
|
|
|
pub struct TestRow {
|
|
pub path: String,
|
|
pub size: u64,
|
|
pub ctime: i64,
|
|
pub mtime: i64,
|
|
pub tags: Vec<String>,
|
|
pub links: Vec<String>,
|
|
pub props: Vec<(String, Value)>,
|
|
}
|
|
|
|
impl TestRow {
|
|
pub fn new(path: &str) -> Self {
|
|
Self { path: path.into(), size: 0, ctime: 0, mtime: 0, tags: vec![], links: vec![], props: vec![] }
|
|
}
|
|
|
|
pub fn prop(mut self, key: &str, value: Value) -> Self {
|
|
self.props.push((key.into(), value));
|
|
self
|
|
}
|
|
|
|
pub fn tags(mut self, tags: &[&str]) -> Self {
|
|
self.tags = tags.iter().map(|t| (*t).to_string()).collect();
|
|
self
|
|
}
|
|
|
|
pub fn links(mut self, links: &[&str]) -> Self {
|
|
self.links = links.iter().map(|t| (*t).to_string()).collect();
|
|
self
|
|
}
|
|
|
|
pub fn row(&self) -> RowRef<'_> {
|
|
RowRef {
|
|
size: self.size,
|
|
ctime_ms: self.ctime,
|
|
mtime_ms: self.mtime,
|
|
tags: &self.tags,
|
|
links: &self.links,
|
|
props: &self.props,
|
|
..RowRef::new(&self.path)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Rows(pub Vec<TestRow>);
|
|
|
|
impl NoteSource for Rows {
|
|
fn len(&self) -> usize {
|
|
self.0.len()
|
|
}
|
|
fn row(&self, i: usize) -> Option<RowRef<'_>> {
|
|
self.0.get(i).map(TestRow::row)
|
|
}
|
|
fn find(&self, path: &str) -> Option<RowRef<'_>> {
|
|
self.0.iter().find(|r| r.path == path).map(TestRow::row)
|
|
}
|
|
fn resolve_link(&self, target: &str, _from: &str) -> Option<String> {
|
|
let with_md = [target, ".md"].concat();
|
|
let found = self.0.iter().find(|r| r.path == target || r.path == with_md || r.row().basename() == target)?;
|
|
Some(found.path.clone())
|
|
}
|
|
}
|
|
|
|
pub fn n(x: f64) -> Value {
|
|
Value::Number(x)
|
|
}
|
|
|
|
pub fn s(x: &str) -> Value {
|
|
Value::Str(x.into())
|
|
}
|
|
|
|
pub fn list(items: Vec<Value>) -> Value {
|
|
Value::List(items)
|
|
}
|
|
|
|
/// Evaluates `src` over `row` with the formulas, `this` and the source of the arguments.
|
|
pub fn run(src: &str, row: &TestRow, formulas: &[(&str, &str)], source: Option<&Rows>, this: Option<&TestRow>) -> Value {
|
|
let expr = notescraft_bases::expr::parse_expr(src).unwrap_or_else(|e| panic!("`{src}` does not parse: {e}"));
|
|
let formulas: Vec<(String, String)> = formulas.iter().map(|(k, v)| ((*k).to_string(), (*v).to_string())).collect();
|
|
let mut env = Env::new(NOW, 7);
|
|
env.this = this.map(TestRow::row);
|
|
env.source = source.map(|r| r as &dyn NoteSource);
|
|
eval(&expr, &env, &row.row(), &Formulas::new(&formulas))
|
|
}
|
|
|
|
/// Evaluates `src` over an empty row.
|
|
pub fn ev(src: &str) -> Value {
|
|
run(src, &TestRow::new("Folder/Note.md"), &[], None, None)
|
|
}
|
|
|
|
/// Asserts every case, naming the source on a failure.
|
|
pub fn cases(table: &[(&str, Value)]) {
|
|
for (src, want) in table {
|
|
assert_eq!(&ev(src), want, "{src}");
|
|
}
|
|
}
|
|
|
|
pub fn is_error(value: &Value) -> bool {
|
|
matches!(value, Value::Error(_))
|
|
}
|