diff --git a/crates/engine/src/datafill.rs b/crates/engine/src/datafill.rs index 6b8376ef..b93e5f59 100644 --- a/crates/engine/src/datafill.rs +++ b/crates/engine/src/datafill.rs @@ -8,7 +8,7 @@ use serde_json::{Value, json}; use crate::cmd::CommandSpec; use crate::params::{get, id, id_of, num_or, string}; -use crate::{Result, Session, bad, spec}; +use crate::{EngineError, Result, Session, bad, spec}; const CMD: &str = "data.fill"; const MAX_ROWS: usize = 500; @@ -31,6 +31,10 @@ fn file_name(name: &str) -> String { } fn fill(s: &mut Session, p: &Value) -> Result { + // Refused before any copy or file is made: a rollback afterwards wouldn't take files back. + if let Some(why) = s.live_read_only(s.ds()?.uid) { + return Err(EngineError::Disabled(CMD.into(), why.into())); + } let src = id(p, "id").ok_or_else(|| bad(CMD, "`id` (the frame to repeat) required"))?; let rows = match get(p, "rows") { Some(Value::Array(a)) if a.iter().all(Value::is_object) && a.len() <= MAX_ROWS => a.clone(), diff --git a/crates/engine/src/live_access_tests.rs b/crates/engine/src/live_access_tests.rs index c2c75bb5..52a08cfd 100644 --- a/crates/engine/src/live_access_tests.rs +++ b/crates/engine/src/live_access_tests.rs @@ -105,6 +105,13 @@ fn a_view_link_makes_the_file_read_only() { refused(&mut s, "shape.rectangle", json!({"x": 0, "y": 0, "width": 5, "height": 5}), VIEW_ONLY); refused(&mut s, "node.set", json!({"ids": [card.0], "props": {"name": "Mine"}}), VIEW_ONLY); refused(&mut s, "edit.delete", json!({"ids": [card.0]}), VIEW_ONLY); + // Refused before it makes copies or writes files, which no rollback takes back. + let dir = std::env::temp_dir().join(format!("figcraft-view-fill-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + refused(&mut s, "data.fill", json!({"id": card.0, "rows": [{"name": "Copy"}], "dir": dir.to_string_lossy()}), VIEW_ONLY); + assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 0, "no file was written"); + let _ = std::fs::remove_dir_all(&dir); refused(&mut s, "tool.set", json!({"tool": "text"}), VIEW_ONLY); assert!(matches!(s.begin_interaction("Move"), Err(EngineError::Disabled(..)))); assert_eq!(*s.doc().unwrap(), before);