coding_tools/cli/
ct_patch.rs1use std::path::PathBuf;
8
9use clap::Parser;
10
11use crate::explain::Format;
12use crate::pattern;
13use crate::pulse::HeartbeatOpts;
14
15#[derive(Parser, Debug)]
16#[command(
17 name = "ct-patch",
18 version,
19 about = "Set/add/delete/move nodes by path in JSON/JSONC/JSONL/YAML, preserving comments and formatting.",
20 long_about = "ct-patch makes structured edits to JSON, JSONC, JSONL, and YAML files (also reachable \
21 as `ct patch`): address a node by path (keys, [N] indices, or [key=value] predicates) \
22 and --set, --add, --delete, or --move-*. JSON-family edits are byte-range splices so \
23 everything outside the changed node is preserved; YAML uses the pure-Rust yaml-edit \
24 backend. Gated by --expect and previewable with --dry-run. See `ct-patch --explain` \
25 for agent-oriented documentation."
26)]
27pub struct Cli {
28 #[arg(long, default_value = ".")]
30 pub base: PathBuf,
31
32 #[arg(long)]
34 pub name: Option<String>,
35
36 #[arg(long, value_enum)]
38 pub mode: Option<pattern::Mode>,
39
40 #[arg(long)]
42 pub hidden: bool,
43
44 #[arg(long)]
46 pub follow: bool,
47
48 #[arg(long, value_name = "PATH=VALUE")]
50 pub set: Vec<String>,
51
52 #[arg(long, value_name = "PATH")]
54 pub delete: Vec<String>,
55
56 #[arg(long, value_name = "PATH=VALUE")]
58 pub add: Vec<String>,
59
60 #[arg(long, value_name = "PATH")]
62 pub move_first: Vec<String>,
63
64 #[arg(long, value_name = "PATH")]
66 pub move_last: Vec<String>,
67
68 #[arg(long, value_name = "PATH")]
70 pub move_up: Vec<String>,
71
72 #[arg(long, value_name = "PATH")]
74 pub move_down: Vec<String>,
75
76 #[arg(long, value_enum)]
78 pub format: Option<DocFormat>,
79
80 #[arg(long)]
82 pub expect: Option<String>,
83
84 #[arg(long)]
86 pub dry_run: bool,
87
88 #[arg(long)]
90 pub quiet: bool,
91
92 #[arg(long)]
94 pub json: bool,
95
96 #[arg(long, value_name = "SECS")]
98 pub timeout: Option<f64>,
99
100 #[command(flatten)]
101 pub heartbeat: HeartbeatOpts,
102
103 #[arg(long, value_enum, num_args = 0..=1, default_missing_value = "md")]
105 pub explain: Option<Format>,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
111pub enum DocFormat {
112 Json,
113 Jsonc,
114 Jsonl,
115 Yaml,
116}
117
118impl DocFormat {
119 pub fn from_ext(ext: &str) -> Option<DocFormat> {
121 match ext.to_ascii_lowercase().as_str() {
122 "json" => Some(DocFormat::Json),
123 "jsonc" => Some(DocFormat::Jsonc),
124 "jsonl" | "ndjson" => Some(DocFormat::Jsonl),
125 "yaml" | "yml" => Some(DocFormat::Yaml),
126 _ => None,
127 }
128 }
129}