use std::path::PathBuf;
use clap::{Parser, Subcommand};
const EXAMPLES: &str = "\
Examples:
pixelcoords freeze all monitors, mark, S saves
pixelcoords --target \"Notepad\" window-relative session
pixelcoords resume pick a saved session, keep editing
pixelcoords assert --session <dir> --point 812,440 --expect submit
pixelcoords assert --session <dir> --stdin < points.txt
pixelcoords resolve --session <dir> --label submit --units auto
pixelcoords emit --session <dir> --format pyautogui
pixelcoords wait --session <dir> --for match --timeout 30s
pixelcoords diff --session <dir> --against baseline
pixelcoords find --session <dir>
pixelcoords doctor --json
Sessions save to Downloads/pixelcoords-captures/<timestamp> unless --out
says otherwise. Output reference:
https://github.com/nolindnaidoo/pixelcoords/blob/main/docs/OUTPUT.md";
#[derive(Debug, Parser)]
#[command(name = "pixelcoords", version, about, after_help = EXAMPLES)]
pub struct Cli {
#[arg(long, value_name = "QUERY", value_delimiter = ',')]
pub monitor: Vec<String>,
#[arg(long, value_name = "TITLE", conflicts_with = "monitor")]
pub target: Option<String>,
#[arg(long, conflicts_with_all = ["target", "monitor"])]
pub pick: bool,
#[arg(long, value_name = "DIR")]
pub out: Option<PathBuf>,
#[arg(long, value_name = "TEXT")]
pub name: Option<String>,
#[arg(long, value_name = "FILE")]
pub config: Option<PathBuf>,
#[arg(
long,
value_name = "SPEC",
help = "Bind a key: KEY=ACTION[,EDGE][,WHEN]; repeatable.\nEDGE: press|release|repeat. WHEN: has_selection|cursor_in"
)]
pub bind: Vec<String>,
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Debug, Subcommand)]
pub enum Command {
Doctor {
#[arg(long, value_name = "FILE")]
config: Option<PathBuf>,
#[arg(long)]
json: bool,
},
Windows {
#[arg(long)]
json: bool,
},
Shoot {
#[arg(long, value_name = "DIR")]
out: Option<PathBuf>,
#[arg(long, value_name = "QUERY", value_delimiter = ',')]
monitor: Vec<String>,
},
Assert {
#[arg(long, value_name = "PATH")]
session: PathBuf,
#[arg(
long,
value_name = "X,Y",
allow_hyphen_values = true,
required_unless_present = "stdin",
conflicts_with = "stdin"
)]
point: Option<String>,
#[arg(long)]
stdin: bool,
#[arg(long, value_name = "TEXT")]
expect: Option<String>,
#[arg(long, value_name = "TEXT", hide = true)]
label: Option<String>,
#[arg(long, value_enum, default_value_t = SpaceArg::Global)]
space: SpaceArg,
#[arg(long, value_name = "INDEX")]
monitor: Option<usize>,
},
Emit {
#[arg(long, value_name = "PATH")]
session: PathBuf,
#[arg(long, value_enum)]
format: FormatArg,
#[arg(long, value_name = "TEXT")]
label: Option<String>,
},
Resume {
#[arg(long, value_name = "PATH|NAME")]
session: Option<PathBuf>,
#[arg(long, conflicts_with = "session")]
last: bool,
#[arg(long, value_name = "DIR")]
out: Option<PathBuf>,
},
Rename {
#[arg(long, value_name = "PATH|NAME")]
session: PathBuf,
#[arg(long, value_name = "TEXT")]
name: String,
},
Resolve {
#[arg(long, value_name = "PATH")]
session: PathBuf,
#[arg(long, value_name = "TEXT")]
label: Option<String>,
#[arg(long, value_enum, default_value_t = SpaceArg::Global)]
space: SpaceArg,
#[arg(long, value_enum, default_value_t = UnitsArg::Auto)]
units: UnitsArg,
#[arg(long)]
relocate: bool,
},
Wait {
#[arg(long, value_name = "PATH")]
session: PathBuf,
#[arg(long, value_name = "TEXT")]
label: Option<String>,
#[arg(long = "for", value_enum, default_value_t = ConditionArg::Match)]
condition: ConditionArg,
#[arg(long, value_name = "DURATION", default_value = "30s")]
timeout: String,
#[arg(long, value_name = "DURATION", default_value = "500ms")]
interval: String,
#[arg(long, value_name = "SCORE", default_value_t = pixelcoords_core::locate::SCORE_FLOOR)]
min_score: f64,
},
Diff {
#[arg(long, value_name = "PATH")]
session: PathBuf,
#[arg(long, value_name = "DIR|IMAGE")]
against: Option<PathBuf>,
#[arg(long, value_name = "TEXT")]
label: Option<String>,
#[arg(long, value_name = "PCT", default_value_t = 0.0)]
tolerance: f64,
},
Find {
#[arg(long, value_name = "PATH")]
session: PathBuf,
#[arg(long, value_name = "TEXT")]
label: Option<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum FormatArg {
Pyautogui,
Cliclick,
Xdotool,
Powershell,
Applescript,
Ydotool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ConditionArg {
Match,
Change,
}
impl From<ConditionArg> for pixelcoords_core::wait::Condition {
fn from(arg: ConditionArg) -> Self {
match arg {
ConditionArg::Match => Self::Match,
ConditionArg::Change => Self::Change,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum UnitsArg {
Auto,
Physical,
Logical,
}
impl From<UnitsArg> for pixelcoords_core::space::Units {
fn from(arg: UnitsArg) -> Self {
match arg {
UnitsArg::Auto => Self::Auto,
UnitsArg::Physical => Self::Physical,
UnitsArg::Logical => Self::Logical,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum SpaceArg {
Global,
Monitor,
Window,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cli_definition_is_consistent() {
use clap::CommandFactory;
Cli::command().debug_assert();
}
}