From 63cb87888b9821c493a5d7eef27a3609e569780699766418f37cd9f95ba1f017 Mon Sep 17 00:00:00 2001 From: kira Date: Thu, 17 Sep 2026 23:42:01 +0200 Subject: [PATCH] Wrapping UI rework and rollback progress addition. --- Cargo.lock | 29 +--- Cargo.toml | 4 +- src/main.rs | 79 +++++---- src/nyan_system.rs | 389 +++++++++++++++++++++++++++++---------------- 4 files changed, 300 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efee12c..918717f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,12 +240,6 @@ dependencies = [ "hashbrown 0.17.1", ] -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - [[package]] name = "nix" version = "0.31.3" @@ -290,14 +284,12 @@ checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f" [[package]] name = "nyanit" -version = "0.2.1" +version = "0.2.2" dependencies = [ "nix", "ratatui", "ratatui-textarea", "regex-lite", - "serde", - "serde_json", "termion", ] @@ -490,19 +482,6 @@ dependencies = [ "syn 3.0.5", ] -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -637,9 +616,3 @@ name = "unicode-width" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index f772d27..68a80e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,9 @@ [package] name = "nyanit" -version = "0.2.1" +version = "0.2.2" edition = "2024" [dependencies] -serde = { version = "1", features = ["derive"] } -serde_json = "1.0" nix = { version = "0.31", features = ["mount"] } ratatui = { version = "0.30", default-features = false, features = ["termion"] } ratatui-textarea = { version = "0.9", default-features = false, features = ["termion"] } diff --git a/src/main.rs b/src/main.rs index ca62f76..fff5918 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,27 +74,29 @@ const CURRENT_BOOT_LIST_TEXT: &str = "bzz current"; // ExitCode::SUCCESS // } -#[derive(Debug, PartialEq)] -struct RollbackState { - progress: f64, - progress_text: String, - snapshot_date_time: String, -} -impl RollbackState { - // Constructor function - pub fn new(snap_date_time: String) -> Self { - RollbackState { - progress: 0.0, - progress_text: "".into(), - snapshot_date_time: snap_date_time, - } - } -} +// #[derive(Debug, PartialEq)] +// struct RollbackState { +// progress: f64, +// progress_text: String, +// snapshot_date_time: String, +// roll_iter: nyan_system::RollBackIterator, +// } +// impl RollbackState { +// // Constructor function +// pub fn new(snaps_list: Vec, snap_date_time: String) -> Self { +// RollbackState { +// progress: 0.0, +// progress_text: "".into(), +// snapshot_date_time: snap_date_time.clone(), +// roll_iter: nyan_system::RollBackIterator::new(snaps_list, snap_date_time) +// } +// } +// } #[derive(Debug, PartialEq)] enum NyanitState { Init, - RollBack(RollbackState), + RollBack, ConfirmRollback(String), WaitForF2, /// list of snapshoots @@ -138,6 +140,7 @@ struct NyanitTUI<'a> { time_counter_sec: u64, textarea: Option>, snaps_list: Vec, + roll_state: Option, } impl NyanitTUI<'_> { @@ -151,6 +154,7 @@ impl NyanitTUI<'_> { time_counter_sec: 0, textarea: None, snaps_list: Vec::new(), + roll_state: None, }) } @@ -351,7 +355,7 @@ impl NyanitTUI<'_> { let par1 = gen_conf_par(snap_dt.clone()); frame.render_widget(par1, vertical_chunks[1]); } - NyanitState::RollBack(roll_state) => { + NyanitState::RollBack => { let centered = vertical_chunks[1].centered_vertically(Constraint::Length(9)); // 1. Create the outer block let block = Block::default() @@ -374,10 +378,11 @@ impl NyanitTUI<'_> { .split(block_inner_area); let gauge = Gauge::default() .gauge_style(Style::default().fg(Color::LightRed)) - .ratio(roll_state.progress); + .ratio(self.roll_state.as_ref().unwrap().progress); frame.render_widget(gauge, chunks[0]); - let text = Paragraph::new(roll_state.progress_text.clone()).centered(); + let text = Paragraph::new(self.roll_state.as_ref().unwrap().progress_text.clone()) + .centered(); frame.render_widget(text, chunks[2]); } NyanitState::EnterPassword => { @@ -489,8 +494,11 @@ impl NyanitTUI<'_> { } NyanitState::ConfirmRollback(snap_dt) => match e { Key::Char('Y') | Key::Char('y') => { - let roll_state = RollbackState::new(snap_dt.clone()); - self.state = NyanitState::RollBack(roll_state); + self.roll_state = Some(nyan_system::RollBackIterator::new( + self.snaps_list.clone(), + snap_dt.clone(), + )); + self.state = NyanitState::RollBack; } Key::Char(_) => self.state = NyanitState::ChooseSnapshot, _ => (), @@ -500,15 +508,26 @@ impl NyanitTUI<'_> { } else { // Tick time thread::sleep(Duration::from_millis(TICK_TIME_MILLS)); - - if let NyanitState::WaitForF2 = self.state { - self.time_counter = self.time_counter.wrapping_add(TICK_TIME_MILLS); - self.time_counter_sec = self.time_counter / 1000; - // wait for F2 - if self.time_counter_sec > WAIT_FOR_F2_SEC { - // boot from root - self.state = NyanitState::HandOff; + match self.state { + NyanitState::WaitForF2 => { + self.time_counter = self.time_counter.wrapping_add(TICK_TIME_MILLS); + self.time_counter_sec = self.time_counter / 1000; + // wait for F2 + if self.time_counter_sec > WAIT_FOR_F2_SEC { + // boot from root + self.state = NyanitState::HandOff; + } } + NyanitState::RollBack => match self.roll_state.as_mut().unwrap().next() { + Some(Err(ex)) => { + self.state = NyanitState::Error(format!("ROLL BACK ERROR!: {}", ex)); + } + None => { + self.state = NyanitState::HandOff; + } + _ => (), + }, + _ => (), } } Ok(()) diff --git a/src/nyan_system.rs b/src/nyan_system.rs index 7773fb4..b71aa67 100644 --- a/src/nyan_system.rs +++ b/src/nyan_system.rs @@ -22,6 +22,7 @@ use nix; use std::fs; use std::process::Command; + /// timeout for block deices population const WAIT_FOR_BLK_DEVS: u64 = 5u64; @@ -343,27 +344,12 @@ pub fn subvolume_delete(subvolume: &str) -> Result<(), ExecCommandError> { /// vol: "usr" or "var" /// dt: datetime /// -fn snap_name_from_dt(vol: &str, dt: &str) -> String { - format!("bzz-{}-{}", vol, dt) -} +// fn snap_name_from_dt(vol: &str, dt: &str) -> String { +// format!("bzz-{}-{}", vol, dt) +// } -/// -/// -/// // Stable Rust example using from_fn -fn first_n(n: u64) -> impl Iterator { - let mut count = 0; - std::iter::from_fn(move || { - if count < n { - let val = count; - count += 1; - Some(val) - } else { - None - } - }) -} - -enum RollbackStates { +#[derive(Debug, PartialEq)] +enum RollbackIterStates { Start, RenameUsrVarAct, ChangeToRw, @@ -374,137 +360,260 @@ enum RollbackStates { Err, } -pub fn roll_back_snap( - snaps_list: &Vec, - target_snap: &str, -) -> impl Iterator> { - let usr_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap); - let var_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap); - - let mut roll_state = RollbackStates::Start; - - std::iter::from_fn(move || match roll_state { - RollbackStates::Start => { - roll_state = RollbackStates::RenameUsrVarAct; - Some(Ok("Move usr and var to temporary location".into())) - } - RollbackStates::RenameUsrVarAct => { - let move_res = fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR) - .and_then(|_| fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)); - if let Err(ex) = move_res { - roll_state = RollbackStates::Err; - Some(Err(ex.into())) - } else { - roll_state = RollbackStates::ChangeToRw; - Some(Ok("Setting snapshot as current system ...".into())) - } - } - RollbackStates::ChangeToRw => { - let rw_res = snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR) - .and_then(|_| snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR)); - if let Err(ex) = rw_res { - roll_state = RollbackStates::Err; - Some(Err(ex)) - } else { - roll_state = RollbackStates::MoveSubvolums; - Some(Ok( - "Moving subvolumes from old /usr and /var to new one".into() - )) - } - } - RollbackStates::MoveSubvolums => { - let mut mv_res = Some(Ok("Removing old /usr and /var".into())); - for (vol, svol_path) in SBUVOL_TO_MOVE { - let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path); - let v_dest = format!("{}/{}{}", TMP_ROOT, vol, svol_path); - if let Err(ex) = fs::rename(v_from, v_dest) { - roll_state = RollbackStates::Err; - mv_res = Some(Err(ex.into())); - break; - } - } - roll_state = RollbackStates::RemoveOldVols; - mv_res - } - RollbackStates::RemoveOldVols => { - let rem_old_res = subvolume_delete(TMP_ROOT_SNAPS_USR) - .and_then(|_| subvolume_delete(TMP_ROOT_SNAPS_VAR)); - if let Err(ex) = rem_old_res { - roll_state = RollbackStates::Err; - Some(Err(ex)) - } else { - roll_state = RollbackStates::ClearSnaps; - Some(Ok("Clearing snapshots...".into())) - } - } - RollbackStates::ClearSnaps => { - let mut clr_snap_res = Some(Ok("All done!".into())); - for snap_date_time in snaps_list.iter().rev() { - let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); - let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); - if let Err(ex) = - subvolume_delete(&usr_snap).and_then(|_| subvolume_delete(&var_snap)) - { - clr_snap_res = Some(Err(ex)); - roll_state = RollbackStates::Err; - break; - } - if snap_date_time == target_snap { - break; - } - } - clr_snap_res - } - RollbackStates::Finish => None, - RollbackStates::Err => None, - }) - - // 1. Rename usr and var to TMP_ROOT_SNAPS/tmp_var|usr - // fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)?; - // fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)?; - - // 2. snapshot desired snaps as RW to /usr and /var - // snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR)?; - // snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR)?; - - // 3. move subvolumes from old /usr and /var - // for (vol, svol_path) in SBUVOL_TO_MOVE { - // let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path); - // let v_dest = format!("{}/{}{}",TMP_ROOT,vol,svol_path); - // fs::rename(v_from, v_dest)?; - // } - - // 4. removing tmp /usr /var - // subvolume_delete(TMP_ROOT_SNAPS_USR)?; - // subvolume_delete(TMP_ROOT_SNAPS_VAR)?; - - // 5. Removing all snapshoots made after one we restored, including inself - // Iterate in reverse untill we get to target_snap in snaps list - // for snap_date_time in snaps_list.iter().rev() { - // let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); - // let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); - // subvolume_delete(&usr_snap)?; - // subvolume_delete(&var_snap)?; - // if snap_date_time == target_snap { - // break; - // } - // } - - // Ok(()) +// 1. Define the iterator struct +#[derive(Debug, PartialEq)] +pub struct RollBackIterator { + pub progress: f64, + pub progress_text: String, + snaps_list: Vec, + target_snap: String, + usr_ro_snap_path: String, + var_ro_snap_path: String, + state: RollbackIterStates, } +impl RollBackIterator { + pub fn new(snaps_list: Vec, target_snap: String) -> Self { + let usr_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", &target_snap); + let var_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "var", &target_snap); + Self { + progress: 0.0, + progress_text: "Starting...".into(), + snaps_list: snaps_list, + target_snap: target_snap, + usr_ro_snap_path: usr_ro_snap_path, + var_ro_snap_path: var_ro_snap_path, + state: RollbackIterStates::Start, + } + } +} + +// 2. Implement Iterator for the iterator struct +impl Iterator for RollBackIterator { + type Item = Result; + + fn next(&mut self) -> Option { + match self.state { + RollbackIterStates::Start => { + self.state = RollbackIterStates::RenameUsrVarAct; + self.progress = 0.0; + self.progress_text = "Move usr and var to temporary location".into(); + Some(Ok(self.progress)) + } + RollbackIterStates::RenameUsrVarAct => { + let move_res = fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR) + .and_then(|_| fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)); + if let Err(ex) = move_res { + self.state = RollbackIterStates::Err; + Some(Err(ex.into())) + } else { + self.state = RollbackIterStates::ChangeToRw; + self.progress_text = "Setting snapshot as current system ...".into(); + self.progress = 0.1; + Some(Ok(self.progress)) + } + } + RollbackIterStates::ChangeToRw => { + let rw_res = snapshot_rw(&self.usr_ro_snap_path, TMP_ROOT_USR) + .and_then(|_| snapshot_rw(&self.var_ro_snap_path, TMP_ROOT_VAR)); + if let Err(ex) = rw_res { + self.state = RollbackIterStates::Err; + Some(Err(ex)) + } else { + self.state = RollbackIterStates::MoveSubvolums; + self.progress_text = + "Moving subvolumes from old /usr and /var to new one".into(); + self.progress = 0.2; + Some(Ok(self.progress)) + } + } + RollbackIterStates::MoveSubvolums => { + self.progress = 0.3; + self.progress_text = "Removing old /usr and /var".into(); + + let mut mv_res = Some(Ok(self.progress)); + for (vol, svol_path) in SBUVOL_TO_MOVE { + let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path); + let v_dest = format!("{}/{}{}", TMP_ROOT, vol, svol_path); + if let Err(ex) = fs::rename(v_from, v_dest) { + self.state = RollbackIterStates::Err; + mv_res = Some(Err(ex.into())); + break; + } + } + self.state = RollbackIterStates::RemoveOldVols; + mv_res + } + RollbackIterStates::RemoveOldVols => { + let rem_old_res = subvolume_delete(TMP_ROOT_SNAPS_USR) + .and_then(|_| subvolume_delete(TMP_ROOT_SNAPS_VAR)); + if let Err(ex) = rem_old_res { + self.state = RollbackIterStates::Err; + Some(Err(ex)) + } else { + self.state = RollbackIterStates::ClearSnaps; + self.progress_text = "Clearing snapshots...".into(); + self.progress = 0.6; + Some(Ok(self.progress)) + } + } + RollbackIterStates::ClearSnaps => { + self.progress = 1.0; + self.progress_text = "All done!".into(); + let mut clr_snap_res = Some(Ok(self.progress)); + for snap_date_time in self.snaps_list.iter().rev() { + let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); + let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); + if let Err(ex) = + subvolume_delete(&usr_snap).and_then(|_| subvolume_delete(&var_snap)) + { + clr_snap_res = Some(Err(ex)); + self.state = RollbackIterStates::Err; + break; + } + if snap_date_time == &self.target_snap { + self.state = RollbackIterStates::Finish; + break; + } + } + clr_snap_res + } + RollbackIterStates::Finish => None, + RollbackIterStates::Err => None, + } + } +} + +// pub fn roll_back_snap( +// snaps_list: Vec, +// target_snap: String, +// ) -> impl Iterator> { +// let usr_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap); +// let var_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap); + +// let mut roll_state = RollbackStates::Start; + +// std::iter::from_fn(move || match roll_state { +// RollbackStates::Start => { +// roll_state = RollbackStates::RenameUsrVarAct; +// Some(Ok("Move usr and var to temporary location".into())) +// } +// RollbackStates::RenameUsrVarAct => { +// let move_res = fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR) +// .and_then(|_| fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)); +// if let Err(ex) = move_res { +// roll_state = RollbackStates::Err; +// Some(Err(ex.into())) +// } else { +// roll_state = RollbackStates::ChangeToRw; +// Some(Ok("Setting snapshot as current system ...".into())) +// } +// } +// RollbackStates::ChangeToRw => { +// let rw_res = snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR) +// .and_then(|_| snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR)); +// if let Err(ex) = rw_res { +// roll_state = RollbackStates::Err; +// Some(Err(ex)) +// } else { +// roll_state = RollbackStates::MoveSubvolums; +// Some(Ok( +// "Moving subvolumes from old /usr and /var to new one".into() +// )) +// } +// } +// RollbackStates::MoveSubvolums => { +// let mut mv_res = Some(Ok("Removing old /usr and /var".into())); +// for (vol, svol_path) in SBUVOL_TO_MOVE { +// let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path); +// let v_dest = format!("{}/{}{}", TMP_ROOT, vol, svol_path); +// if let Err(ex) = fs::rename(v_from, v_dest) { +// roll_state = RollbackStates::Err; +// mv_res = Some(Err(ex.into())); +// break; +// } +// } +// roll_state = RollbackStates::RemoveOldVols; +// mv_res +// } +// RollbackStates::RemoveOldVols => { +// let rem_old_res = subvolume_delete(TMP_ROOT_SNAPS_USR) +// .and_then(|_| subvolume_delete(TMP_ROOT_SNAPS_VAR)); +// if let Err(ex) = rem_old_res { +// roll_state = RollbackStates::Err; +// Some(Err(ex)) +// } else { +// roll_state = RollbackStates::ClearSnaps; +// Some(Ok("Clearing snapshots...".into())) +// } +// } +// RollbackStates::ClearSnaps => { +// let mut clr_snap_res = Some(Ok("All done!".into())); +// for snap_date_time in snaps_list.iter().rev() { +// let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); +// let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); +// if let Err(ex) = +// subvolume_delete(&usr_snap).and_then(|_| subvolume_delete(&var_snap)) +// { +// clr_snap_res = Some(Err(ex)); +// roll_state = RollbackStates::Err; +// break; +// } +// if snap_date_time == &target_snap { +// break; +// } +// } +// clr_snap_res +// } +// RollbackStates::Finish => None, +// RollbackStates::Err => None, +// }) + +// // 1. Rename usr and var to TMP_ROOT_SNAPS/tmp_var|usr +// // fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)?; +// // fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)?; + +// // 2. snapshot desired snaps as RW to /usr and /var +// // snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR)?; +// // snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR)?; + +// // 3. move subvolumes from old /usr and /var +// // for (vol, svol_path) in SBUVOL_TO_MOVE { +// // let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path); +// // let v_dest = format!("{}/{}{}",TMP_ROOT,vol,svol_path); +// // fs::rename(v_from, v_dest)?; +// // } + +// // 4. removing tmp /usr /var +// // subvolume_delete(TMP_ROOT_SNAPS_USR)?; +// // subvolume_delete(TMP_ROOT_SNAPS_VAR)?; + +// // 5. Removing all snapshoots made after one we restored, including inself +// // Iterate in reverse untill we get to target_snap in snaps list +// // for snap_date_time in snaps_list.iter().rev() { +// // let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); +// // let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time); +// // subvolume_delete(&usr_snap)?; +// // subvolume_delete(&var_snap)?; +// // if snap_date_time == target_snap { +// // break; +// // } +// // } + +// // Ok(()) +// } + /// /// Checks if pac_lock file exist and returns date-time from it if it does, /// Otherwise return None -/// +/// pub fn check_pac_lock() -> Result, ExecCommandError> { use std::path::Path; let pac_lock_path = Path::new(PAC_LOCK_FILE); if pac_lock_path.exists() { let snap_dt = fs::read_to_string(pac_lock_path)?; Ok(Some(snap_dt.trim().into())) - } - else { + } else { Ok(None) } }