mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-31 13:27:16 -06:00
This commit is contained in:
@@ -151,18 +151,33 @@ pub fn playback_plan(s: &Session, p: &Value) -> Result<(Action, usize)> {
|
||||
Ok((action, from))
|
||||
}
|
||||
|
||||
/// The actions that `steps` call through Play Action steps, followed transitively, in list order,
|
||||
/// so a droplet can run without the Actions panel that made it (#2794). Index references in
|
||||
/// `steps` and in the copies become names, which resolve without the rest of the list. A target
|
||||
/// that doesn't resolve is left alone: it fails at playback, as it would have before.
|
||||
/// The actions that `steps` call through Play Action steps and the branches of Conditional steps,
|
||||
/// followed transitively, in list order, so a droplet can run without the Actions panel that made
|
||||
/// it (#2794, #6804). Index references in `steps` and in the copies become names, which resolve
|
||||
/// without the rest of the list. A target that doesn't resolve is left alone: it fails at
|
||||
/// playback, as it would have before.
|
||||
pub fn called_actions(list: &[Action], steps: &mut [(String, Value)]) -> Vec<Action> {
|
||||
fn targets(list: &[Action], steps: &mut [(String, Value)], depth: usize, queue: &mut Vec<(usize, usize)>) {
|
||||
for (_, params) in steps.iter_mut().filter(|(id, _)| id == "actions.play") {
|
||||
let Ok(i) = resolve(list, params, "actions.play") else { continue };
|
||||
if let (Some(action), Some(obj)) = (list.get(i), params.as_object_mut()) {
|
||||
obj.insert("action".into(), json!(action.name));
|
||||
for (id, params) in steps.iter_mut() {
|
||||
match id.as_str() {
|
||||
"actions.play" => {
|
||||
let Ok(i) = resolve(list, params, "actions.play") else { continue };
|
||||
if let (Some(action), Some(obj)) = (list.get(i), params.as_object_mut()) {
|
||||
obj.insert("action".into(), json!(action.name));
|
||||
}
|
||||
queue.push((i, depth));
|
||||
}
|
||||
// Conditional branches only take names, so there is nothing to rewrite.
|
||||
"actions.conditional" => {
|
||||
for key in ["then", "else"] {
|
||||
let Some(name) = params.get(key).and_then(Value::as_str) else { continue };
|
||||
if let Some(i) = list.iter().position(|a| a.name == name) {
|
||||
queue.push((i, depth));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
queue.push((i, depth));
|
||||
}
|
||||
}
|
||||
let mut queue = Vec::new();
|
||||
|
||||
@@ -558,7 +558,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"file.automate.createDroplet",
|
||||
"Create Droplet…",
|
||||
&["File", "Automate"],
|
||||
r##"{"path":str (.pcdroplet),"steps":[[id,params]…] (the action),"name":str?,"output":folder?,"format":"same|png|jpg|…"?,"quality":0..12?,"shim":bool=true on Unix (writes <name>.command calling `photocraft-cli droplet`)} → {path, shim} (the actions its Play Action steps call are saved in the droplet)"##,
|
||||
r##"{"path":str (.pcdroplet),"steps":[[id,params]…] (the action),"name":str?,"output":folder?,"format":"same|png|jpg|…"?,"quality":0..12?,"shim":bool=true on Unix (writes <name>.command calling `photocraft-cli droplet`)} → {path, shim} (the actions its Play Action and Conditional steps call are saved in the droplet)"##,
|
||||
native,
|
||||
create_droplet
|
||||
),
|
||||
|
||||
@@ -252,6 +252,38 @@ fn droplets_carry_the_actions_their_play_steps_call() {
|
||||
assert_eq!(r["files"].as_array().unwrap().len(), inputs.len(), "{r}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn droplets_carry_the_actions_their_conditional_steps_call() {
|
||||
use crate::actions_cmds::Action;
|
||||
let dir = tmp("droplet-conditional");
|
||||
let inputs = images(&dir, 2, 40, 20);
|
||||
let mut s = Session::new();
|
||||
let act = |name: &str, steps: Value| -> Action { serde_json::from_value(json!({"name": name, "steps": steps})).unwrap() };
|
||||
s.actions.list = vec![
|
||||
act("Unrelated", json!([["image.imageRotation.180", {}]])),
|
||||
act("Rotate", json!([["image.imageRotation.90cw", {}]])),
|
||||
// The then branch calls Rotate by index, as the panel records it.
|
||||
act("Wide", json!([["actions.play", {"action": 1}]])),
|
||||
act("Tall", json!([["image.imageRotation.180", {}]])),
|
||||
];
|
||||
let steps = json!([["actions.conditional", {"if": "landscape", "then": "Wide", "else": "Tall"}]]);
|
||||
let out = format!("{dir}/out");
|
||||
let r = s
|
||||
.execute("file.automate.createDroplet", json!({"path": format!("{dir}/Cond"), "steps": steps, "format": "png", "output": out, "shim": false}))
|
||||
.unwrap();
|
||||
let dp = r["path"].as_str().unwrap().to_string();
|
||||
let v: Value = serde_json::from_slice(&std::fs::read(&dp).unwrap()).unwrap();
|
||||
let names: Vec<&str> = v["calledActions"].as_array().unwrap().iter().map(|a| a["name"].as_str().unwrap()).collect();
|
||||
assert_eq!(names, ["Rotate", "Wide", "Tall"], "both branches and what they call");
|
||||
|
||||
// A fresh session, as `photocraft-cli droplet` and the droplet shim use.
|
||||
let mut fresh = Session::new();
|
||||
let r = fresh.execute("file.automate.runDroplet", json!({"droplet": dp, "input": [dir.clone()]})).unwrap();
|
||||
assert_eq!(r["files"].as_array().unwrap().len(), inputs.len(), "{r}");
|
||||
let img = photocraft_codecs::decode(&std::fs::read(format!("{out}/img0.png")).unwrap()).unwrap();
|
||||
assert_eq!(img.dimensions(), (20, 40), "the landscape input took the then branch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_and_droplets_skip_recorded_view_steps() {
|
||||
let dir = tmp("view-steps");
|
||||
|
||||
Reference in New Issue
Block a user