mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 11:17:01 -06:00
Variables: a data set with no value for any bound variable shows a status message
Choosing a data set in the Variables panel, stepping through data sets with the panel's arrows, and running `dataset.select`, `dataset.next` and `dataset.prev` applied nothing and showed no message when no object was bound to a variable that the data set has a value for. This happens, for example, after Import Data Sets… defines new variables without binding them. The app now shows "nothing in data set A was applied: no object is bound to a variable it has a value for" in the status bar, and the three commands add it to the reply as `warning`. A data set with a value for a bound variable gives no warning even when every object bound to it is locked; `skipped` counts those objects. What applying a data set does is unchanged. Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
@@ -459,7 +459,7 @@ fn dataset_list(s: &mut Session, _p: &Value) -> Result<Value> {
|
||||
|
||||
fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
const C: &str = "dataset.select";
|
||||
let (applied, skipped, mut typed) = s.edit("Apply Data Set", |d, sel| {
|
||||
let (applied, skipped, mut typed, bound) = s.edit("Apply Data Set", |d, sel| {
|
||||
let ds = d.variables.dataset(name).cloned().ok_or_else(|| bad(C, format!("no dataset named `{name}`")))?;
|
||||
// One object may be driven by several variables; walk them in definition order, so two
|
||||
// text variables on the same object land the later one's value and both are counted.
|
||||
@@ -467,6 +467,8 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
for v in &d.variables.variables {
|
||||
pairs.extend(d.variables.objects_of(&v.name).into_iter().map(|id| (id, v.name.clone())));
|
||||
}
|
||||
// Whether an object is bound to a variable that this row has a value for (the reply's `warning`).
|
||||
let bound = pairs.iter().any(|(_, var)| ds.values.contains_key(var));
|
||||
let mut applied = 0usize;
|
||||
let mut skipped = 0usize;
|
||||
// Collects the type objects whose text the row rewrote, for the reply's `overflow`.
|
||||
@@ -514,12 +516,18 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
d.variables.active_dataset = Some(name.to_string());
|
||||
// Every selected object left hidden or locked, such as one the row hid, is deselected.
|
||||
sel.deselect_uneditable(d);
|
||||
Ok((applied, skipped, typed))
|
||||
Ok((applied, skipped, typed, bound))
|
||||
})?;
|
||||
// Two text variables on one object rewrite it twice; it is named once.
|
||||
typed.sort_unstable();
|
||||
typed.dedup();
|
||||
Ok(super::typecmd::with_overflow(s, &typed, json!({"name": name, "applied": applied, "skipped": skipped})))
|
||||
let mut out = super::typecmd::with_overflow(s, &typed, json!({"name": name, "applied": applied, "skipped": skipped}));
|
||||
// A row with no value for any bound variable applies nothing. The reply then carries a
|
||||
// `warning`, which the app shows in its status bar.
|
||||
if !bound {
|
||||
out["warning"] = json!(format!("nothing in data set {name} was applied: no object is bound to a variable it has a value for"));
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn dataset_select(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
@@ -1037,7 +1045,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Select Data Set",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{name} apply a dataset in one undo step, deselecting every selected object left hidden or locked (the key object too) → {applied, skipped, overflow?: [id…] (area type and type on a path whose text it rewrote and that no longer fits; left out when all fits)}",
|
||||
"{name} apply a dataset in one undo step, deselecting every selected object left hidden or locked (the key object too) → {applied, skipped, overflow?: [id…] (area type and type on a path whose text it rewrote and that no longer fits; left out when all fits), warning? (when no object is bound to a variable that the dataset has a value for, so nothing was applied)}",
|
||||
has_doc,
|
||||
dataset_select
|
||||
),
|
||||
|
||||
@@ -131,6 +131,44 @@ fn mismatched_and_missing_bindings_are_skipped_and_counted() {
|
||||
assert_eq!(plain(&s, t), "y");
|
||||
}
|
||||
|
||||
/// A row with no value for any bound variable, such as one from Import Data Sets… before anything
|
||||
/// is bound, applies nothing, and the reply's `warning` names it. A bound variable with a value in
|
||||
/// the row gives no warning, even when its object is locked and skipped.
|
||||
#[test]
|
||||
fn a_row_with_no_value_for_a_bound_variable_warns_that_nothing_was_applied() {
|
||||
let mut s = session();
|
||||
let t = text(&mut s, "x");
|
||||
s.execute("dataset.import", &json!({"csv": "name,Name,Title\nA,Ann,Boss\nB,Bob,Chief\n"})).unwrap();
|
||||
let warning = |row: &str| format!("nothing in data set {row} was applied: no object is bound to a variable it has a value for");
|
||||
let v = s.execute("dataset.select", &json!({"name": "A"})).unwrap();
|
||||
assert_eq!((v["applied"].as_u64(), v["skipped"].as_u64(), &v["warning"]), (Some(0), Some(0), &json!(warning("A"))), "{v}");
|
||||
let v = s.execute("dataset.next", &json!({})).unwrap();
|
||||
assert_eq!(v["warning"], json!(warning("B")), "{v}");
|
||||
let v = s.execute("dataset.prev", &json!({})).unwrap();
|
||||
assert_eq!(v["warning"], json!(warning("A")), "{v}");
|
||||
assert_eq!(plain(&s, t), "x");
|
||||
|
||||
// A bound variable that the row has no value for still applies nothing.
|
||||
s.execute("variable.define", &json!({"name": "Other", "kind": "text"})).unwrap();
|
||||
s.execute("variable.bind", &json!({"variable": "Other", "ids": [t.0]})).unwrap();
|
||||
let v = s.execute("dataset.select", &json!({"name": "B"})).unwrap();
|
||||
assert_eq!((v["applied"].as_u64(), v["skipped"].as_u64(), &v["warning"]), (Some(0), Some(1), &json!(warning("B"))), "{v}");
|
||||
|
||||
// Bound to a variable with a value, the row applies it and gives no warning.
|
||||
s.execute("variable.bind", &json!({"variable": "Title", "ids": [t.0]})).unwrap();
|
||||
let v = s.execute("dataset.select", &json!({"name": "A"})).unwrap();
|
||||
assert_eq!(v["applied"].as_u64(), Some(1), "{v}");
|
||||
assert!(v.get("warning").is_none(), "{v}");
|
||||
assert_eq!(plain(&s, t), "Boss");
|
||||
|
||||
// Locked art is skipped, and its binding still counts as bound.
|
||||
s.execute("select.set", &json!({"ids": [t.0]})).unwrap();
|
||||
s.execute("object.lock", &json!({})).unwrap();
|
||||
let v = s.execute("dataset.select", &json!({"name": "B"})).unwrap();
|
||||
assert_eq!(v["applied"].as_u64(), Some(0), "{v}");
|
||||
assert!(v.get("warning").is_none(), "{v}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deleting_a_variable_cleans_bindings_values_and_leaves_datasets_alone() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user