Skip to main content

launcher/headless/
snapshot.rs

1use std::path::PathBuf;
2use std::process::ExitCode;
3use std::thread;
4
5use crate::utils::create_storage;
6use serde_json;
7use supreme_fs::SnapshotEvent;
8use supreme_snapshot::SnapshotManifest;
9
10/// Handle snapshot list command
11pub fn run_snapshot_list() -> ExitCode {
12    let storage = create_storage();
13
14    match storage.list_snapshots_profiles() {
15        Ok(profiles) => {
16            if profiles.is_empty() {
17                println!("No local snapshots found.");
18            } else {
19                println!("Local snapshots ({}):", profiles.len());
20                for profile in profiles {
21                    // Use get_profile() so remote/online profiles are handled consistently.
22                    if let Ok(content) = storage.get_profile(&profile)
23                        && let Ok(manifest) = serde_json::from_slice::<SnapshotManifest>(&content)
24                    {
25                        let name = manifest.name.unwrap_or_else(|| "Unnamed".to_string());
26                        let desc = manifest
27                            .description
28                            .unwrap_or_else(|| "No description".to_string());
29                        let version = manifest.version.unwrap_or_else(|| "Unknown".to_string());
30                        println!(
31                            "  {} (name: {}, version: {}, files: {})",
32                            profile,
33                            name,
34                            version,
35                            manifest.files.len()
36                        );
37                        println!("    Description: {}", desc);
38                        continue;
39                    }
40                    println!("  {}", profile);
41                }
42            }
43            ExitCode::SUCCESS
44        }
45        Err(e) => {
46            eprintln!("Error listing snapshots: {e}");
47            ExitCode::FAILURE
48        }
49    }
50}
51
52/// Handle snapshot create command
53pub fn run_snapshot_create(
54    name: String,
55    description: Option<String>,
56    version: Option<String>,
57    compression: u32,
58    directory: Option<PathBuf>,
59) -> ExitCode {
60    let storage = create_storage();
61    let (tx, rx) = std::sync::mpsc::channel::<SnapshotEvent>();
62
63    println!("Creating snapshot '{}'...", name);
64
65    // Spawn thread for progress reporting
66    let progress_handle = thread::spawn(move || {
67        while let Ok(event) = rx.recv() {
68            match event {
69                SnapshotEvent::SetStatus(msg) => {
70                    println!("Status: {}", msg);
71                }
72                SnapshotEvent::Log(msg) => {
73                    println!("Log: {}", msg);
74                }
75                SnapshotEvent::InitProgress(total) => {
76                    println!("Progress: 0/{} files", total);
77                }
78                SnapshotEvent::IncrementProgress => {
79                    // Progress update - don't print for every increment
80                }
81                SnapshotEvent::Perf(op, target, ms) => {
82                    println!("Perf: {} -> {} in {}ms", op, target, ms);
83                }
84            }
85        }
86    });
87
88    match supreme_snapshot::create_snapshot(
89        &storage,
90        &name,
91        description,
92        version,
93        directory,
94        tx,
95        compression,
96    ) {
97        Ok(hash) => {
98            if progress_handle.join().is_err() {
99                eprintln!("Progress reporting thread panicked");
100            }
101            println!("Snapshot created successfully! Hash: {}", hash);
102            ExitCode::SUCCESS
103        }
104        Err(e) => {
105            if progress_handle.join().is_err() {
106                eprintln!("Progress reporting thread panicked");
107            }
108            eprintln!("Failed to create snapshot: {e}");
109            ExitCode::FAILURE
110        }
111    }
112}
113
114/// Handle snapshot restore command
115pub fn run_snapshot_restore(profile: String) -> ExitCode {
116    let storage = create_storage();
117    let (tx, rx) = std::sync::mpsc::channel::<SnapshotEvent>();
118
119    println!("Restoring snapshot '{}'...", profile);
120
121    // Spawn thread for progress reporting
122    let progress_handle = thread::spawn(move || {
123        while let Ok(event) = rx.recv() {
124            match event {
125                SnapshotEvent::SetStatus(msg) => {
126                    println!("Status: {}", msg);
127                }
128                SnapshotEvent::Log(msg) => {
129                    println!("Log: {}", msg);
130                }
131                SnapshotEvent::InitProgress(total) => {
132                    println!("Progress: 0/{} files", total);
133                }
134                SnapshotEvent::IncrementProgress => {
135                    // Progress update
136                }
137                SnapshotEvent::Perf(op, target, ms) => {
138                    println!("Perf: {} -> {} in {}ms", op, target, ms);
139                }
140            }
141        }
142    });
143
144    let result = supreme_snapshot::restore_snapshot(&storage, &profile, tx);
145    // tx was moved into restore_snapshot; dropping it here (implicitly) lets progress_handle finish.
146    if progress_handle.join().is_err() {
147        eprintln!("Progress reporting thread panicked");
148    }
149
150    match result {
151        Ok(manifest) => {
152            // Handle music reconciliation using the manifest already in memory.
153            let (music_tx, music_rx) = std::sync::mpsc::channel::<SnapshotEvent>();
154            let music_handle = thread::spawn(move || {
155                while let Ok(event) = music_rx.recv() {
156                    if let SnapshotEvent::Log(msg) = event {
157                        println!("Music: {}", msg);
158                    }
159                }
160            });
161
162            if let Err(e) =
163                supreme_snapshot::reconcile_music_after_restore(&storage, &manifest, &music_tx)
164            {
165                eprintln!("Failed to reconcile music after restore: {e}");
166            }
167            drop(music_tx);
168            if music_handle.join().is_err() {
169                eprintln!("Music reconciliation logging thread panicked");
170            }
171
172            println!("Snapshot restored successfully!");
173            ExitCode::SUCCESS
174        }
175        Err(e) => {
176            eprintln!("Failed to restore snapshot: {e}");
177            ExitCode::FAILURE
178        }
179    }
180}