mirror of
https://gitlab.com/erkanisik/yali-rs.git
synced 2026-07-31 03:09:00 +00:00
feat: add sub-progress bar for rsync file copy progress
- InstallMessage: added SubProgress(f32) and CurrentFile(String) variants - GlobalState: added install_sub_progress field - UiSender: added sub_progress() and current_file() helpers - CopyFilesJob: now streams rsync stderr, parses xfr#/to-chk for progress, and sends SubProgress/CurrentFile messages to UI - execution.rs: displays secondary progress bar below the main one during rsync file copy, updates install_current_file in real-time - main.rs: handles new InstallMessage variants in auto-install mode
This commit is contained in:
@@ -207,6 +207,7 @@ pub struct GlobalState {
|
|||||||
pub current_step: usize,
|
pub current_step: usize,
|
||||||
pub available_disks: Vec<DiskInfo>,
|
pub available_disks: Vec<DiskInfo>,
|
||||||
pub install_progress: f32,
|
pub install_progress: f32,
|
||||||
|
pub install_sub_progress: f32,
|
||||||
pub install_log: Vec<String>,
|
pub install_log: Vec<String>,
|
||||||
pub install_current_file: Option<String>,
|
pub install_current_file: Option<String>,
|
||||||
pub demo_mode: bool,
|
pub demo_mode: bool,
|
||||||
@@ -274,6 +275,7 @@ impl Default for GlobalState {
|
|||||||
current_step: 0,
|
current_step: 0,
|
||||||
available_disks: Vec::new(),
|
available_disks: Vec::new(),
|
||||||
install_progress: 0.0,
|
install_progress: 0.0,
|
||||||
|
install_sub_progress: 0.0,
|
||||||
install_log: Vec::new(),
|
install_log: Vec::new(),
|
||||||
install_current_file: None,
|
install_current_file: None,
|
||||||
demo_mode: false,
|
demo_mode: false,
|
||||||
|
|||||||
+74
-2
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
|
|
||||||
@@ -27,6 +28,10 @@ pub enum InstallMessage {
|
|||||||
Log(String),
|
Log(String),
|
||||||
/// Genel ilerleme; 0.0–1.0 arasında.
|
/// Genel ilerleme; 0.0–1.0 arasında.
|
||||||
Progress(f32),
|
Progress(f32),
|
||||||
|
/// Alt ilerleme (ör: rsync içindeki dosya kopyalama); 0.0–1.0 arasında.
|
||||||
|
SubProgress(f32),
|
||||||
|
/// Kopyalanmakta olan dosya adı.
|
||||||
|
CurrentFile(String),
|
||||||
/// Tüm işler başarıyla tamamlandı.
|
/// Tüm işler başarıyla tamamlandı.
|
||||||
Done,
|
Done,
|
||||||
/// Bir iş başarısız oldu; kurulum durdu.
|
/// Bir iş başarısız oldu; kurulum durdu.
|
||||||
@@ -54,6 +59,14 @@ impl UiSender {
|
|||||||
pub fn log(&self, s: impl Into<String>) {
|
pub fn log(&self, s: impl Into<String>) {
|
||||||
self.send(InstallMessage::Log(s.into()));
|
self.send(InstallMessage::Log(s.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sub_progress(&self, p: f32) {
|
||||||
|
self.send(InstallMessage::SubProgress(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_file(&self, f: impl Into<String>) {
|
||||||
|
self.send(InstallMessage::CurrentFile(f.into()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
@@ -179,8 +192,67 @@ impl Job for CopyFilesJob {
|
|||||||
args.push(self.source.clone());
|
args.push(self.source.clone());
|
||||||
args.push(self.target.clone());
|
args.push(self.target.clone());
|
||||||
|
|
||||||
let arg_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
|
if DEMO_MODE.load(Ordering::Relaxed) {
|
||||||
run_cmd("rsync", &arg_refs).await?;
|
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut child = Command::new("rsync")
|
||||||
|
.args(&args)
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.map_err(|e| format!("rsync çalıştırılamadı: {}", e))?;
|
||||||
|
|
||||||
|
let stderr = child.stderr.take()
|
||||||
|
.ok_or_else(|| "rsync stderr alınamadı".to_string())?;
|
||||||
|
let reader = BufReader::new(stderr);
|
||||||
|
let mut lines = reader.lines();
|
||||||
|
|
||||||
|
while let Some(line) = lines.next_line().await
|
||||||
|
.map_err(|e| format!("rsync çıktısı okunamadı: {}", e))?
|
||||||
|
{
|
||||||
|
let trimmed = line.trim();
|
||||||
|
// rsync --info=progress2 format (newlines when piped):
|
||||||
|
// 1,024 100% 512.00kB/s 0:00:00 (xfr#5, to-chk=99/105)
|
||||||
|
// Extract xfr# and to-chk for sub-progress
|
||||||
|
if let Some(xfr_pos) = trimmed.find("xfr#") {
|
||||||
|
let after_xfr = &trimmed[xfr_pos + 4..];
|
||||||
|
let xfr_end = after_xfr.find(|c: char| !c.is_ascii_digit()).unwrap_or(after_xfr.len());
|
||||||
|
let xfr: u64 = after_xfr[..xfr_end].parse().unwrap_or(0);
|
||||||
|
|
||||||
|
if let Some(to_chk_pos) = trimmed.find("to-chk=") {
|
||||||
|
let after_to = &trimmed[to_chk_pos + 7..];
|
||||||
|
let slash_pos = after_to.find('/').unwrap_or(after_to.len());
|
||||||
|
let total_end = after_to[slash_pos + 1..]
|
||||||
|
.find(|c: char| !c.is_ascii_digit())
|
||||||
|
.unwrap_or(after_to[slash_pos + 1..].len());
|
||||||
|
let total: u64 = after_to[slash_pos + 1..][..total_end].parse().unwrap_or(1);
|
||||||
|
|
||||||
|
let p = if total > 0 {
|
||||||
|
(xfr as f32) / (total as f32)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
ui.sub_progress(p.min(1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract filename: lines starting with a path (not a number/space)
|
||||||
|
let first_char = trimmed.chars().next();
|
||||||
|
if let Some('/') = first_char {
|
||||||
|
ui.current_file(trimmed.to_string());
|
||||||
|
} else if trimmed.starts_with("./") || trimmed.starts_with("..") {
|
||||||
|
ui.current_file(trimmed.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = child.wait().await
|
||||||
|
.map_err(|e| format!("rsync beklenemedi: {}", e))?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
return Err(format!("rsync çıkış kodu: {:?}", status.code()));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,8 @@ fn run_auto_install(path: &str, demo_mode: bool) -> eframe::Result<()> {
|
|||||||
print!("\r {}: [{:50}] {:.0}%", t!("progress"), "=".repeat((p * 50.0) as usize), p * 100.0);
|
print!("\r {}: [{:50}] {:.0}%", t!("progress"), "=".repeat((p * 50.0) as usize), p * 100.0);
|
||||||
let _ = std::io::Write::flush(&mut std::io::stdout());
|
let _ = std::io::Write::flush(&mut std::io::stdout());
|
||||||
}
|
}
|
||||||
|
Ok(InstallMessage::SubProgress(_)) => {}
|
||||||
|
Ok(InstallMessage::CurrentFile(_)) => {}
|
||||||
Ok(InstallMessage::Done) => {
|
Ok(InstallMessage::Done) => {
|
||||||
println!("\n\n{}: {}", t!("installation_done"), t!("installation_done_description"));
|
println!("\n\n{}: {}", t!("installation_done"), t!("installation_done_description"));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ impl InstallerStep for ExecutionStep {
|
|||||||
self.slideshow = Some(Slideshow::new(&desktop, state.branding_icons.clone(), state.branding_slides.clone()));
|
self.slideshow = Some(Slideshow::new(&desktop, state.branding_icons.clone(), state.branding_slides.clone()));
|
||||||
|
|
||||||
state.install_progress = 0.0;
|
state.install_progress = 0.0;
|
||||||
|
state.install_sub_progress = 0.0;
|
||||||
state.install_log.clear();
|
state.install_log.clear();
|
||||||
state.install_current_file = None;
|
state.install_current_file = None;
|
||||||
state.install_log.push(t!("install_starting").to_string());
|
state.install_log.push(t!("install_starting").to_string());
|
||||||
@@ -182,6 +183,12 @@ impl InstallerStep for ExecutionStep {
|
|||||||
Ok(InstallMessage::Progress(p)) => {
|
Ok(InstallMessage::Progress(p)) => {
|
||||||
state.install_progress = p;
|
state.install_progress = p;
|
||||||
}
|
}
|
||||||
|
Ok(InstallMessage::SubProgress(p)) => {
|
||||||
|
state.install_sub_progress = p;
|
||||||
|
}
|
||||||
|
Ok(InstallMessage::CurrentFile(f)) => {
|
||||||
|
state.install_current_file = Some(f);
|
||||||
|
}
|
||||||
Ok(InstallMessage::Done) => {
|
Ok(InstallMessage::Done) => {
|
||||||
state.install_progress = 1.0;
|
state.install_progress = 1.0;
|
||||||
self.finished = true;
|
self.finished = true;
|
||||||
@@ -305,6 +312,16 @@ impl InstallerStep for ExecutionStep {
|
|||||||
.animate(running)
|
.animate(running)
|
||||||
.desired_height(10.0),
|
.desired_height(10.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if state.install_sub_progress > 0.0 {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.add(
|
||||||
|
egui::ProgressBar::new(state.install_sub_progress)
|
||||||
|
.animate(running)
|
||||||
|
.desired_height(6.0)
|
||||||
|
.fill(theme::c_accent_dim()),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Durum etiketi
|
// Durum etiketi
|
||||||
|
|||||||
Reference in New Issue
Block a user