mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-29 20:56:47 -06:00
CLI: run rejects an export option with no export after it (#67)
--page, --scale and --pdf-options apply to the exports that follow them; one given after the last --export or --all-pages did nothing and said nothing. The usage text and docs/agents.md say so.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
//! Headless DesignCraft.
|
||||
//!
|
||||
//! ```text
|
||||
//! designcraft-cli run [--in FILE.designcraft|FILE.idml | --sample] [--cmd ID[=JSON]]... [--page N] [--scale S] [--export OUT.png|.jpg|.pdf|.designcraft|.idml|.epub] [--pdf-options JSON] [--all-pages DIR]
|
||||
//! designcraft-cli run [--in FILE.designcraft|FILE.idml | --sample] [--cmd ID[=JSON]]... [--page N] [--scale S] [--pdf-options JSON] [--export OUT.png|.jpg|.pdf|.designcraft|.idml|.epub] [--all-pages DIR]
|
||||
//! # --page, --scale and --pdf-options apply to the exports that follow them
|
||||
//! designcraft-cli commands [FILTER] # list commands (JSON), optionally only ids/labels/menus containing FILTER
|
||||
//! designcraft-cli describe ID # one command: label, menu, shortcut, parameters
|
||||
//! designcraft-cli script [FILE|-] [--in FILE | --sample] [--connect PORT] [--save OUT] [--export OUT] [--keep-going]
|
||||
@@ -77,7 +78,7 @@ fn main() -> ExitCode {
|
||||
}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"usage: designcraft-cli run [--in FILE | --sample] [--cmd ID[=JSON]]... [--page N] [--scale S] [--pdf-options JSON] [--export OUT] [--all-pages DIR]\n designcraft-cli commands [FILTER]\n designcraft-cli describe COMMAND\n designcraft-cli script [FILE|-] [--in FILE | --sample] [--connect PORT] [--save OUT] [--export OUT] [--keep-going]\n designcraft-cli app [--port PORT] COMMAND [JSON] | --method METHOD [JSON]\n designcraft-cli mcp [--connect PORT] [--sample]\n designcraft-cli perf [--pages N] [--runs N] [--strict]\n designcraft-cli bench FILE [--runs N]\n designcraft-cli links\n designcraft-cli --version"
|
||||
"usage: designcraft-cli run [--in FILE | --sample] [--cmd ID[=JSON]]... [--page N] [--scale S] [--pdf-options JSON] [--export OUT] [--all-pages DIR]\n (--page, --scale and --pdf-options apply to the exports that follow them)\n designcraft-cli commands [FILTER]\n designcraft-cli describe COMMAND\n designcraft-cli script [FILE|-] [--in FILE | --sample] [--connect PORT] [--save OUT] [--export OUT] [--keep-going]\n designcraft-cli app [--port PORT] COMMAND [JSON] | --method METHOD [JSON]\n designcraft-cli mcp [--connect PORT] [--sample]\n designcraft-cli perf [--pages N] [--runs N] [--strict]\n designcraft-cli bench FILE [--runs N]\n designcraft-cli links\n designcraft-cli --version"
|
||||
);
|
||||
eprintln!(
|
||||
"\nCommunity: {} · {} · {}",
|
||||
@@ -136,7 +137,36 @@ fn mcp(args: &[String]) -> Result<(), String> {
|
||||
Server::new(backend).serve(stdin.lock(), stdout.lock()).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// The first `--page`, `--scale` or `--pdf-options` with no `--export` or `--all-pages` after it.
|
||||
/// They apply to the exports that follow them, so one after the last export would do nothing.
|
||||
fn option_after_last_export(args: &[String]) -> Option<&str> {
|
||||
let mut unused = None;
|
||||
let mut it = args.iter();
|
||||
while let Some(a) = it.next() {
|
||||
match a.as_str() {
|
||||
"--page" | "--scale" | "--pdf-options" => {
|
||||
unused = unused.or(Some(a.as_str()));
|
||||
it.next();
|
||||
}
|
||||
"--export" | "--all-pages" => {
|
||||
unused = None;
|
||||
it.next();
|
||||
}
|
||||
"--in" | "--cmd" => {
|
||||
it.next();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
unused
|
||||
}
|
||||
|
||||
fn run(args: &[String]) -> Result<(), String> {
|
||||
if let Some(opt) = option_after_last_export(args) {
|
||||
return Err(format!(
|
||||
"{opt} has no --export or --all-pages after it; options apply to the exports that follow them, so put it before its --export"
|
||||
));
|
||||
}
|
||||
let mut s = Session::new();
|
||||
let mut page = 0usize;
|
||||
let mut scale = 1.0f64;
|
||||
|
||||
@@ -80,3 +80,25 @@ fn closed_stdout_ends_quietly() {
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(out.status.success() && !err.contains("panicked"), "{:?}: {err}", out.status);
|
||||
}
|
||||
|
||||
/// `--scale`, `--page` and `--pdf-options` apply to the exports after them; one with no export
|
||||
/// after it was silently ignored.
|
||||
#[test]
|
||||
fn run_export_options_come_before_their_export() {
|
||||
let dir = std::env::temp_dir().join(format!("dc-cli-order-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let (a, b) = (dir.join("a.png"), dir.join("b.png"));
|
||||
let (a, b) = (a.to_str().unwrap(), b.to_str().unwrap());
|
||||
for (opt, val) in [("--scale", "0.5"), ("--page", "1"), ("--pdf-options", r#"{"pages":"1"}"#)] {
|
||||
let out = cli().args(["run", "--sample", "--export", a, opt, val]).output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success(), "{opt} after the last --export: {err}");
|
||||
assert!(err.contains(opt) && err.contains("before"), "{err}");
|
||||
}
|
||||
assert!(!std::path::Path::new(a).exists(), "nothing is exported when the arguments are wrong");
|
||||
let out = cli().args(["run", "--sample", "--scale", "0.5", "--export", a, "--scale", "0.25", "--export", b]).output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(out.status.success(), "{err}");
|
||||
assert!(err.contains("wrote") && std::path::Path::new(a).exists() && std::path::Path::new(b).exists(), "{err}");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ The result is JSON: `{"completed": n, "results": [...]}`, plus `failedIndex` / `
|
||||
fails (the exit status is non-zero; `--keep-going` records errors and continues). `run --cmd ID=JSON` accepts the
|
||||
same references, and MCP's `batch` tool takes `commands` or `script` text with them.
|
||||
|
||||
`run` works through its arguments in order. `--page`, `--scale` and `--pdf-options` apply to the `--export` /
|
||||
`--all-pages` that follow them, so one run can write several sizes
|
||||
(`run --sample --scale 0.5 --export small.png --scale 2 --export large.png`); one with no export after it is an error.
|
||||
|
||||
## Data merge
|
||||
|
||||
`data.source.select {path, sheet?}` links a CSV/TSV/`.xlsx` file to the document (`data.source.update` re-reads it,
|
||||
|
||||
Reference in New Issue
Block a user