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}