use std::path::PathBuf;
use clap::Parser;
use crate::explain::Format;
use crate::pattern;
use crate::pulse::HeartbeatOpts;
#[derive(Parser, Debug)]
#[command(
name = "ct-patch",
version,
about = "Set/add/delete/move nodes by path in JSON/JSONC/JSONL/YAML, preserving comments and formatting.",
long_about = "ct-patch makes structured edits to JSON, JSONC, JSONL, and YAML files (also reachable \
as `ct patch`): address a node by path (keys, [N] indices, or [key=value] predicates) \
and --set, --add, --delete, or --move-*. JSON-family edits are byte-range splices so \
everything outside the changed node is preserved; YAML uses the pure-Rust yaml-edit \
backend. Gated by --expect and previewable with --dry-run. See `ct-patch --explain` \
for agent-oriented documentation."
)]
pub struct Cli {
#[arg(long, default_value = ".")]
pub base: PathBuf,
#[arg(long)]
pub name: Option<String>,
#[arg(long, value_enum)]
pub mode: Option<pattern::Mode>,
#[arg(long)]
pub hidden: bool,
#[arg(long)]
pub follow: bool,
#[arg(long, value_name = "PATH=VALUE")]
pub set: Vec<String>,
#[arg(long, value_name = "PATH")]
pub delete: Vec<String>,
#[arg(long, value_name = "PATH=VALUE")]
pub add: Vec<String>,
#[arg(long, value_name = "PATH")]
pub move_first: Vec<String>,
#[arg(long, value_name = "PATH")]
pub move_last: Vec<String>,
#[arg(long, value_name = "PATH")]
pub move_up: Vec<String>,
#[arg(long, value_name = "PATH")]
pub move_down: Vec<String>,
#[arg(long, value_enum)]
pub format: Option<DocFormat>,
#[arg(long)]
pub expect: Option<String>,
#[arg(long)]
pub dry_run: bool,
#[arg(long)]
pub quiet: bool,
#[arg(long)]
pub json: bool,
#[arg(long, value_name = "SECS")]
pub timeout: Option<f64>,
#[command(flatten)]
pub heartbeat: HeartbeatOpts,
#[arg(long, value_enum, num_args = 0..=1, default_missing_value = "md")]
pub explain: Option<Format>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum DocFormat {
Json,
Jsonc,
Jsonl,
Yaml,
}
impl DocFormat {
pub fn from_ext(ext: &str) -> Option<DocFormat> {
match ext.to_ascii_lowercase().as_str() {
"json" => Some(DocFormat::Json),
"jsonc" => Some(DocFormat::Jsonc),
"jsonl" | "ndjson" => Some(DocFormat::Jsonl),
"yaml" | "yml" => Some(DocFormat::Yaml),
_ => None,
}
}
}