chub_cli/commands/
snapshot.rs1use clap::{Args, Subcommand};
2use owo_colors::OwoColorize;
3
4use chub_core::team::snapshots;
5
6use crate::output;
7
8#[derive(Args)]
9pub struct SnapshotArgs {
10 #[command(subcommand)]
11 command: SnapshotCommand,
12}
13
14#[derive(Subcommand)]
15pub enum SnapshotCommand {
16 Create(SnapshotCreateArgs),
18 Restore(SnapshotRestoreArgs),
20 Diff(SnapshotDiffArgs),
22 List,
24}
25
26#[derive(Args)]
27pub struct SnapshotCreateArgs {
28 name: String,
30}
31
32#[derive(Args)]
33pub struct SnapshotRestoreArgs {
34 name: String,
36}
37
38#[derive(Args)]
39pub struct SnapshotDiffArgs {
40 from: String,
42 to: String,
44}
45
46pub fn run(args: SnapshotArgs, json: bool) {
47 match args.command {
48 SnapshotCommand::Create(a) => run_create(a, json),
49 SnapshotCommand::Restore(a) => run_restore(a, json),
50 SnapshotCommand::Diff(a) => run_diff(a, json),
51 SnapshotCommand::List => run_list(json),
52 }
53}
54
55fn run_create(args: SnapshotCreateArgs, json: bool) {
56 match snapshots::create_snapshot(&args.name) {
57 Ok(snap) => {
58 if json {
59 println!(
60 "{}",
61 serde_json::json!({
62 "status": "created",
63 "name": snap.name,
64 "pins": snap.pins.len(),
65 "created_at": snap.created_at,
66 })
67 );
68 } else {
69 output::success(&format!(
70 "Snapshot \"{}\" created with {} pins.",
71 snap.name.bold(),
72 snap.pins.len()
73 ));
74 }
75 }
76 Err(e) => {
77 output::error(&e.to_string(), json);
78 std::process::exit(1);
79 }
80 }
81}
82
83fn run_restore(args: SnapshotRestoreArgs, json: bool) {
84 match snapshots::restore_snapshot(&args.name) {
85 Ok(snap) => {
86 if json {
87 println!(
88 "{}",
89 serde_json::json!({
90 "status": "restored",
91 "name": snap.name,
92 "pins": snap.pins.len(),
93 })
94 );
95 } else {
96 output::success(&format!(
97 "Restored snapshot \"{}\": {} pins.",
98 snap.name.bold(),
99 snap.pins.len()
100 ));
101 }
102 }
103 Err(e) => {
104 output::error(&e.to_string(), json);
105 std::process::exit(1);
106 }
107 }
108}
109
110fn run_diff(args: SnapshotDiffArgs, json: bool) {
111 match snapshots::diff_snapshots(&args.from, &args.to) {
112 Ok(diffs) => {
113 if json {
114 println!(
115 "{}",
116 serde_json::to_string_pretty(&serde_json::json!({
117 "from": args.from,
118 "to": args.to,
119 "changes": diffs,
120 }))
121 .unwrap_or_default()
122 );
123 } else {
124 if diffs.is_empty() {
125 eprintln!("{}", "No changes between snapshots.".dimmed());
126 return;
127 }
128 eprintln!(
129 "{}\n",
130 format!("{} → {} ({} changes):", args.from, args.to, diffs.len()).bold()
131 );
132 for diff in &diffs {
133 match &diff.change {
134 snapshots::DiffChange::Added { version } => {
135 let v = version.as_deref().unwrap_or("latest");
136 eprintln!(" {} {} ({})", "+".green(), diff.id.bold(), v);
137 }
138 snapshots::DiffChange::Removed { version } => {
139 let v = version.as_deref().unwrap_or("latest");
140 eprintln!(" {} {} ({})", "-".red(), diff.id.bold(), v);
141 }
142 snapshots::DiffChange::Changed {
143 from_version,
144 to_version,
145 } => {
146 let from = from_version.as_deref().unwrap_or("?");
147 let to = to_version.as_deref().unwrap_or("?");
148 eprintln!(" {} {} ({} → {})", "~".yellow(), diff.id.bold(), from, to);
149 }
150 }
151 }
152 }
153 }
154 Err(e) => {
155 output::error(&e.to_string(), json);
156 std::process::exit(1);
157 }
158 }
159}
160
161fn run_list(json: bool) {
162 let snapshots_list = snapshots::list_snapshots();
163
164 if json {
165 let items: Vec<serde_json::Value> = snapshots_list
166 .iter()
167 .map(|(name, created_at)| {
168 serde_json::json!({
169 "name": name,
170 "created_at": created_at,
171 })
172 })
173 .collect();
174 println!(
175 "{}",
176 serde_json::to_string_pretty(&serde_json::json!({
177 "snapshots": items,
178 "total": snapshots_list.len(),
179 }))
180 .unwrap_or_default()
181 );
182 } else {
183 if snapshots_list.is_empty() {
184 eprintln!(
185 "{}",
186 "No snapshots. Use `chub snapshot create <name>` to create one.".dimmed()
187 );
188 return;
189 }
190 eprintln!(
191 "{}",
192 format!("{} snapshots:\n", snapshots_list.len()).bold()
193 );
194 for (name, created_at) in &snapshots_list {
195 eprintln!(" {} {}", name.bold(), created_at.dimmed());
196 }
197 }
198}