use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use crate::flake::template::regenerate_flake;
use crate::state::PackageState;
static COMPLETED: AtomicBool = AtomicBool::new(false);
static ROLLBACK_CONTEXT: Mutex<Option<RollbackContext>> = Mutex::new(None);
#[derive(Clone)]
pub struct RollbackContext {
pub flake_dir: PathBuf,
pub state_path: PathBuf,
pub original_state: PackageState,
pub copied_file: Option<PathBuf>,
pub created_dir: Option<PathBuf>,
}
pub fn init_signal_handler() {
if let Err(e) = ctrlc::set_handler(move || {
if COMPLETED.load(Ordering::SeqCst) {
std::process::exit(130);
}
if let Some(ctx) = take_context() {
eprintln!("\nInterrupted. Rolling back changes...");
perform_rollback(&ctx);
}
std::process::exit(130); }) {
eprintln!(
"Warning: Failed to set Ctrl+C handler: {}. Rollback on interrupt will be unavailable.",
e
);
}
}
pub fn set_context(ctx: RollbackContext) {
COMPLETED.store(false, Ordering::SeqCst);
if let Ok(mut guard) = ROLLBACK_CONTEXT.lock() {
*guard = Some(ctx);
}
}
pub fn clear_context() {
if let Ok(mut guard) = ROLLBACK_CONTEXT.lock() {
*guard = None;
}
COMPLETED.store(true, Ordering::SeqCst);
}
fn take_context() -> Option<RollbackContext> {
ROLLBACK_CONTEXT
.lock()
.ok()
.and_then(|mut guard| guard.take())
}
fn perform_rollback(ctx: &RollbackContext) {
if let Err(e) = ctx.original_state.save(&ctx.state_path) {
eprintln!("Warning: Failed to restore packages.json: {}", e);
}
if let Some(ref path) = ctx.copied_file {
let _ = std::fs::remove_file(path);
}
if let Some(ref path) = ctx.created_dir {
let _ = std::fs::remove_dir_all(path);
}
if let Err(e) = regenerate_flake(&ctx.flake_dir, &ctx.original_state) {
eprintln!("Warning: Failed to restore flake.nix: {}", e);
}
eprintln!("Rollback complete.");
}