1use clap::{Args, Subcommand};
2use color_eyre::Result;
3use comfy_table::Table;
4use envx_core::{EnvVarManager, SnapshotManager};
5
6#[derive(Args)]
7pub struct SnapshotArgs {
8 #[command(subcommand)]
9 pub command: SnapshotCommands,
10}
11
12#[derive(Subcommand)]
13pub enum SnapshotCommands {
14 Create {
16 name: String,
18 #[arg(short, long)]
20 description: Option<String>,
21 },
22 List,
24 Show {
26 snapshot: String,
28 },
29 Restore {
31 snapshot: String,
33 #[arg(short, long)]
35 force: bool,
36 },
37 Delete {
39 snapshot: String,
41 #[arg(short, long)]
43 force: bool,
44 },
45 Diff {
47 snapshot1: String,
49 snapshot2: String,
51 },
52}
53
54pub fn handle_snapshot(args: SnapshotArgs) -> Result<()> {
66 let snapshot_manager = SnapshotManager::new()?;
67 let mut env_manager = EnvVarManager::new();
68 env_manager.load_all()?;
69
70 match args.command {
71 SnapshotCommands::Create { name, description } => {
72 let vars = env_manager.list().into_iter().cloned().collect();
73 let snapshot = snapshot_manager.create(name, description, vars)?;
74 println!("✅ Created snapshot: {} (ID: {})", snapshot.name, snapshot.id);
75 }
76 SnapshotCommands::List => {
77 let snapshots = snapshot_manager.list()?;
78 if snapshots.is_empty() {
79 println!("No snapshots found.");
80 return Ok(());
81 }
82
83 let mut table = Table::new();
84 table.set_header(vec!["Name", "ID", "Created", "Variables", "Description"]);
85
86 for snapshot in snapshots {
87 table.add_row(vec![
88 snapshot.name,
89 snapshot.id[..8].to_string(),
90 snapshot.created_at.format("%Y-%m-%d %H:%M").to_string(),
91 snapshot.variables.len().to_string(),
92 snapshot.description.unwrap_or_default(),
93 ]);
94 }
95
96 println!("{table}");
97 }
98 SnapshotCommands::Show { snapshot } => {
99 let snap = snapshot_manager.get(&snapshot)?;
100 println!("Snapshot: {}", snap.name);
101 println!("ID: {}", snap.id);
102 println!("Created: {}", snap.created_at.format("%Y-%m-%d %H:%M:%S"));
103 println!("Description: {}", snap.description.unwrap_or_default());
104 println!("Variables: {}", snap.variables.len());
105
106 println!("\nFirst 10 variables:");
108 for (i, (name, var)) in snap.variables.iter().take(10).enumerate() {
109 println!(" {}. {} = {}", i + 1, name, var.value);
110 }
111
112 if snap.variables.len() > 10 {
113 println!(" ... and {} more", snap.variables.len() - 10);
114 }
115 }
116 SnapshotCommands::Restore { snapshot, force } => {
117 if !force {
118 print!("⚠️ This will replace all current environment variables. Continue? [y/N] ");
119 std::io::Write::flush(&mut std::io::stdout())?;
120
121 let mut input = String::new();
122 std::io::stdin().read_line(&mut input)?;
123 if !input.trim().eq_ignore_ascii_case("y") {
124 println!("Cancelled.");
125 return Ok(());
126 }
127 }
128
129 snapshot_manager.restore(&snapshot, &mut env_manager)?;
130 println!("✅ Restored from snapshot: {snapshot}");
131 }
132 SnapshotCommands::Delete { snapshot, force } => {
133 if !force {
134 print!("⚠️ Delete snapshot '{snapshot}'? [y/N] ");
135 std::io::Write::flush(&mut std::io::stdout())?;
136
137 let mut input = String::new();
138 std::io::stdin().read_line(&mut input)?;
139 if !input.trim().eq_ignore_ascii_case("y") {
140 println!("Cancelled.");
141 return Ok(());
142 }
143 }
144
145 snapshot_manager.delete(&snapshot)?;
146 println!("✅ Deleted snapshot: {snapshot}");
147 }
148 SnapshotCommands::Diff { snapshot1, snapshot2 } => {
149 let diff = snapshot_manager.diff(&snapshot1, &snapshot2)?;
150
151 if diff.added.is_empty() && diff.removed.is_empty() && diff.modified.is_empty() {
152 println!("No differences found between snapshots.");
153 return Ok(());
154 }
155
156 if !diff.added.is_empty() {
157 println!("➕ Added in {snapshot2}:");
158 for (name, var) in &diff.added {
159 println!(" {} = {}", name, var.value);
160 }
161 }
162
163 if !diff.removed.is_empty() {
164 println!("\n➖ Removed in {snapshot2}:");
165 for (name, var) in &diff.removed {
166 println!(" {} = {}", name, var.value);
167 }
168 }
169
170 if !diff.modified.is_empty() {
171 println!("\n🔄 Modified:");
172 for (name, (old, new)) in &diff.modified {
173 println!(" {name}:");
174 println!(" Old: {}", old.value);
175 println!(" New: {}", new.value);
176 }
177 }
178 }
179 }
180
181 Ok(())
182}