server_forge/
rollback.rs

1//! # Rollback Module
2//!
3//! This module provides functionality for creating system snapshots and rolling back changes.
4//! It allows the application to revert the system state in case of failures during the setup process.
5
6use crate::distro::{get_package_manager, uninstall_package};
7use log::info;
8use std::cell::RefCell;
9use std::error::Error;
10use std::fs;
11
12/// Manages the creation of snapshots and rollback operations.
13pub struct RollbackManager {
14    snapshots: RefCell<Vec<Snapshot>>,
15}
16
17/// Represents a system snapshot, containing information about changed files and installed packages.
18struct Snapshot {
19    files_changed: Vec<(String, Vec<u8>)>, // (file path, original content)
20    packages_installed: Vec<String>,
21}
22
23impl RollbackManager {
24    /// Creates a new `RollbackManager` instance.
25    pub fn new() -> Self {
26        RollbackManager {
27            snapshots: RefCell::new(Vec::new()),
28        }
29    }
30
31    /// Creates a new snapshot and returns its ID.
32    ///
33    /// # Errors
34    ///
35    /// Returns an error if the snapshot creation fails.
36    pub fn create_snapshot(&self) -> Result<usize, Box<dyn Error>> {
37        let snapshot = Snapshot {
38            files_changed: Vec::new(),
39            packages_installed: Vec::new(),
40        };
41        self.snapshots.borrow_mut().push(snapshot);
42        Ok(self.snapshots.borrow().len() - 1)
43    }
44
45    /// Adds a file change to a specific snapshot.
46    ///
47    /// # Arguments
48    ///
49    /// * `snapshot_id` - The ID of the snapshot to add the file change to
50    /// * `file_path` - The path of the changed file
51    ///
52    /// # Errors
53    ///
54    /// Returns an error if reading the file fails or if the snapshot ID is invalid.
55    pub fn add_file_change(
56        &self,
57        snapshot_id: usize,
58        file_path: &str,
59    ) -> Result<(), Box<dyn Error>> {
60        let original_content = fs::read(file_path)?;
61        self.snapshots.borrow_mut()[snapshot_id]
62            .files_changed
63            .push((file_path.to_string(), original_content));
64        Ok(())
65    }
66
67    /// Adds an installed package to a specific snapshot.
68    ///
69    /// # Arguments
70    ///
71    /// * `snapshot_id` - The ID of the snapshot to add the package to
72    /// * `package` - The name of the installed package
73    ///
74    /// # Errors
75    ///
76    /// Returns an error if the snapshot ID is invalid.
77    pub fn add_package_installed(
78        &self,
79        snapshot_id: usize,
80        package: &str,
81    ) -> Result<(), Box<dyn Error>> {
82        self.snapshots.borrow_mut()[snapshot_id]
83            .packages_installed
84            .push(package.to_string());
85        Ok(())
86    }
87
88    /// Commits a snapshot, finalizing its state.
89    ///
90    /// This method is a placeholder and currently does nothing.
91    /// It could be expanded to compress the snapshot or write it to disk.
92    ///
93    /// # Arguments
94    ///
95    /// * `_snapshot_id` - The ID of the snapshot to commit
96    pub fn commit_snapshot(&self, _snapshot_id: usize) -> Result<(), Box<dyn Error>> {
97        // we could compress the snapshot or write it to disk here
98        Ok(())
99    }
100
101    /// Rolls back all changes made since the first snapshot.
102    ///
103    /// # Errors
104    ///
105    /// Returns an error if any part of the rollback process fails.
106    pub fn rollback_all(&self) -> Result<(), Box<dyn Error>> {
107        info!("Rolling back all changes...");
108
109        for snapshot in self.snapshots.borrow().iter().rev() {
110            self.rollback_snapshot(snapshot)?;
111        }
112
113        info!("Rollback completed");
114        Ok(())
115    }
116
117    /// Rolls back changes made in a specific snapshot.
118    ///
119    /// # Arguments
120    ///
121    /// * `snapshot` - A reference to the `Snapshot` to roll back
122    ///
123    /// # Errors
124    ///
125    /// Returns an error if any part of the rollback process fails.
126    fn rollback_snapshot(&self, snapshot: &Snapshot) -> Result<(), Box<dyn Error>> {
127        // Rollback file changes
128        for (file_path, original_content) in &snapshot.files_changed {
129            info!("Rolling back changes to file: {}", file_path);
130            fs::write(file_path, original_content)?;
131        }
132
133        // Uninstall packages
134        let package_manager = get_package_manager()?;
135        for package in &snapshot.packages_installed {
136            info!("Uninstalling package: {}", package);
137            uninstall_package(&package_manager, package)?;
138        }
139
140        Ok(())
141    }
142
143    /// Rolls back changes to a specific snapshot.
144    ///
145    /// # Arguments
146    ///
147    /// * `snapshot_id` - The ID of the snapshot to roll back to
148    ///
149    /// # Errors
150    ///
151    /// Returns an error if the snapshot ID is invalid or if any part of the rollback process fails.
152    pub fn rollback_to(&self, snapshot_id: usize) -> Result<(), Box<dyn Error>> {
153        info!("Rolling back to snapshot {}", snapshot_id);
154
155        let snapshots = self.snapshots.borrow();
156        if snapshot_id >= snapshots.len() {
157            return Err("Invalid snapshot ID".into());
158        }
159
160        for snapshot in snapshots.iter().skip(snapshot_id).rev() {
161            self.rollback_snapshot(snapshot)?;
162        }
163
164        info!("Rollback to snapshot {} completed", snapshot_id);
165        Ok(())
166    }
167}