forked from pisilinux-rs/yali-rs
Please provide the file changes or a description of the modifications you made so I can generate the commit message for you.
This commit is contained in:
+132
-29
@@ -11,6 +11,7 @@
|
||||
//! UI'nin hemen güncellenmesi sağlanır.
|
||||
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
@@ -140,6 +141,43 @@ impl Job for CopyFilesJob {
|
||||
fn name(&self) -> &str { "job_copy_files" }
|
||||
|
||||
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
||||
ui.log(format!("[DEBUG] CopyFilesJob başladı"));
|
||||
ui.log(format!("[DEBUG] Kaynak: '{}'", self.source));
|
||||
ui.log(format!("[DEBUG] Hedef: '{}'", self.target));
|
||||
|
||||
// Kaynak dizini kontrol et
|
||||
let source_path = std::path::Path::new(&self.source);
|
||||
let source_exists = source_path.exists();
|
||||
let source_readable = source_exists && source_path.is_dir();
|
||||
ui.log(format!("[DEBUG] Kaynak mevcut: {}, dizin: {}", source_exists, source_readable));
|
||||
|
||||
if !source_exists {
|
||||
ui.log(format!("[DEBUG] Kaynak dizin içeriği aranıyor..."));
|
||||
// Kaynağın bir üst dizinini dene
|
||||
let parent = source_path.parent();
|
||||
if let Some(p) = parent {
|
||||
if p.exists() {
|
||||
if let Ok(entries) = std::fs::read_dir(p) {
|
||||
for entry in entries.flatten().take(20) {
|
||||
ui.log(format!("[DEBUG] /{:?}", entry.path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui.log("[DEBUG] Kaynağın parent'ı yok (kök dizin olabilir)");
|
||||
}
|
||||
} else if source_readable {
|
||||
// İlk 10 girdiyi listele
|
||||
if let Ok(entries) = std::fs::read_dir(source_path) {
|
||||
let mut count = 0;
|
||||
for entry in entries.flatten().take(10) {
|
||||
ui.log(format!("[DEBUG] {:?}", entry.path()));
|
||||
count += 1;
|
||||
}
|
||||
ui.log(format!("[DEBUG] (ilk {} girdi listelendi)", count));
|
||||
}
|
||||
}
|
||||
|
||||
ui.log(format!("rsync {} → {}", self.source, self.target));
|
||||
|
||||
let is_root_source = self.source.trim_end_matches('/') == "";
|
||||
@@ -204,53 +242,113 @@ impl Job for CopyFilesJob {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut child = Command::new("rsync")
|
||||
.args(&args)
|
||||
// rsync çıktısı pipe'a yönlendirilince tam tamponlu (fully buffered) olur,
|
||||
// bu da progress çıktısının gerçek zamanlı gelmemesine yol açar.
|
||||
// stdbuf -eL ile stderr'i satır tamponlu (line-buffered) yapıyoruz.
|
||||
let (program, final_args) = if Path::new("/usr/bin/stdbuf").exists() {
|
||||
let mut a = vec!["-eL".to_string(), "rsync".to_string()];
|
||||
a.extend(args);
|
||||
("stdbuf", a)
|
||||
} else {
|
||||
("rsync", args)
|
||||
};
|
||||
|
||||
let mut child = Command::new(program)
|
||||
.args(&final_args)
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|e| format!("rsync çalıştırılamadı: {}", e))?;
|
||||
|
||||
let stdout = child.stdout.take()
|
||||
.ok_or_else(|| "rsync stdout alınamadı".to_string())?;
|
||||
let stderr = child.stderr.take()
|
||||
.ok_or_else(|| "rsync stderr alınamadı".to_string())?;
|
||||
|
||||
// Stderr'i arka planda okuyarak tamponun dolmasını ve rsync'in kilitlenmesini engelliyoruz.
|
||||
let stderr_err_accumulator = std::sync::Arc::new(tokio::sync::Mutex::new(String::new()));
|
||||
let stderr_accumulator_clone = stderr_err_accumulator.clone();
|
||||
tokio::spawn(async move {
|
||||
let reader = BufReader::new(stderr);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
let mut accum = stderr_accumulator_clone.lock().await;
|
||||
accum.push_str(&line);
|
||||
accum.push('\n');
|
||||
}
|
||||
});
|
||||
|
||||
let reader = BufReader::new(stdout);
|
||||
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);
|
||||
// Pipe'a yönlendirilince rsync satırbaşı (\r) içerebilir.
|
||||
// Önce \r karakterlerini temizle, sonra trim yap.
|
||||
let cleaned: String = line.chars()
|
||||
.filter(|&c| c != '\r')
|
||||
.collect();
|
||||
let trimmed = cleaned.trim().to_string();
|
||||
|
||||
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);
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let p = if total > 0 {
|
||||
(xfr as f32) / (total as f32)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// ── 1) Progress satırı mı? ──────────────────────────
|
||||
// rsync --info=progress2 format (pipe yoluyla):
|
||||
// " 1,024 100% 512.00kB/s 0:00:00 (xfr#5, to-chk=99/105)"
|
||||
// " 3.456M 67% 5.12MB/s 0:00:01 (xfr#17, to-chk=83/105)"
|
||||
// (xfr# ve to-chk= varsa progress satırıdır)
|
||||
if trimmed.contains("xfr#") && trimmed.contains("to-chk=") {
|
||||
// xfr# değerini çıkar
|
||||
let xfr = if let Some(pos) = trimmed.find("xfr#") {
|
||||
let after = &trimmed[pos + 4..];
|
||||
let end = after.find(|c: char| !c.is_ascii_digit()).unwrap_or(after.len());
|
||||
after[..end].parse::<u64>().unwrap_or(0)
|
||||
} else { 0 };
|
||||
|
||||
// to-chk= içinden total değerini çıkar
|
||||
let total = if let Some(pos) = trimmed.find("to-chk=") {
|
||||
let after = &trimmed[pos + 7..];
|
||||
let slash_pos = after.find('/');
|
||||
if let Some(sp) = slash_pos {
|
||||
let after_slash = &after[sp + 1..];
|
||||
let end = after_slash.find(|c: char| !c.is_ascii_digit()).unwrap_or(after_slash.len());
|
||||
after_slash[..end].parse::<u64>().unwrap_or(1)
|
||||
} else { 1 }
|
||||
} else { 1 };
|
||||
|
||||
if total > 0 {
|
||||
let p = (xfr as f32) / (total as f32);
|
||||
ui.sub_progress(p.min(1.0));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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());
|
||||
// ── 2) Progress satırının ilk kısmı mı? (sayı + yüzde ile başlayan) ──
|
||||
// rsync pipe çıktısı bazen iki satıra bölünebilir:
|
||||
// " 1,024 100% 512.00kB/s 0:00:00"
|
||||
// " (xfr#5, to-chk=99/105)"
|
||||
// Bu durumda ilk satırda sadece sayı/yüzde/hız bilgisi vardır, atla.
|
||||
{
|
||||
let first = trimmed.chars().next().unwrap_or(' ');
|
||||
if first.is_ascii_digit() || first == ' ' {
|
||||
// Sayı veya boşlukla başlayıp xfr içermiyorsa progress'in devamıdır
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 3) Gerçek dosya adı ──────────────────────────────
|
||||
// rsync --out-format kullanılmadığında dosya adları da stderr'e
|
||||
// göreceli yol olarak yazılır: "etc/passwd", "usr/share/doc/..."
|
||||
// Bunlar genellikle '.' '/' veya harf ile başlar, sayı ile başlamaz.
|
||||
// progress satırı olmadığına göre bu bir dosya adıdır.
|
||||
if trimmed.len() > 3 && !trimmed.starts_with('[') && !trimmed.contains("sending incremental")
|
||||
&& !trimmed.contains("building file list") && !trimmed.contains("done")
|
||||
&& !trimmed.contains("sent ") && !trimmed.contains("total size")
|
||||
{
|
||||
ui.current_file(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +356,12 @@ impl Job for CopyFilesJob {
|
||||
.map_err(|e| format!("rsync beklenemedi: {}", e))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(format!("rsync çıkış kodu: {:?}", status.code()));
|
||||
let stderr_content = stderr_err_accumulator.lock().await.clone();
|
||||
return Err(format!(
|
||||
"rsync çıkış kodu: {:?}. Hata: {}",
|
||||
status.code(),
|
||||
stderr_content.trim()
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -178,6 +178,10 @@ impl InstallerStep for ExecutionStep {
|
||||
loop {
|
||||
match rx.try_recv() {
|
||||
Ok(InstallMessage::Log(line)) => {
|
||||
if line.starts_with('▶') {
|
||||
state.install_sub_progress = 0.0;
|
||||
state.install_current_file = None;
|
||||
}
|
||||
state.install_log.push(line);
|
||||
}
|
||||
Ok(InstallMessage::Progress(p)) => {
|
||||
@@ -268,8 +272,7 @@ impl InstallerStep for ExecutionStep {
|
||||
|
||||
// İlerleme çubuğu
|
||||
let running = !self.finished
|
||||
&& self.error_screen.is_none()
|
||||
&& state.install_progress > 0.0;
|
||||
&& self.error_screen.is_none();
|
||||
|
||||
egui::Frame::none()
|
||||
.fill(theme::c_bg_widget())
|
||||
@@ -313,7 +316,6 @@ impl InstallerStep for ExecutionStep {
|
||||
.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)
|
||||
@@ -321,7 +323,6 @@ impl InstallerStep for ExecutionStep {
|
||||
.desired_height(6.0)
|
||||
.fill(theme::c_accent_dim()),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Durum etiketi
|
||||
|
||||
Reference in New Issue
Block a user