From dfa7c463acd3a559457f95b51a1bc2cc61378bff Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 30 Mar 2026 13:58:33 +0300 Subject: [PATCH] feat(all): Add loging --- src/main.rs | 14 +++++ src/operations.rs | 20 +------ src/operations/check.rs | 2 - src/operations/fileinfo.rs | 19 +++++++ src/operations/info.rs | 1 - src/operations/local_fs.rs | 11 ++-- src/operations/remotes.rs | 23 +++++++- src/operations/sync.rs | 1 - src/rclone_install/rclone_install_real.rs | 61 ++++++++++++++++------ src/rclone_install/rclone_install_tests.rs | 1 - 10 files changed, 108 insertions(+), 45 deletions(-) create mode 100644 src/operations/fileinfo.rs diff --git a/src/main.rs b/src/main.rs index 2e31609..59ca8c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,13 @@ use eframe::egui; use egui::{FontData, FontDefinitions, FontFamily}; fn main() -> Result<(), eframe::Error> { + // Инициализация логгера + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) + .format_timestamp_millis() + .init(); + + log::info!("Запуск Rclone UI Manager"); + let viewport = egui::ViewportBuilder::default() .with_inner_size([1200.0, 800.0]) .with_min_inner_size([800.0, 600.0]) @@ -20,11 +27,15 @@ fn main() -> Result<(), eframe::Error> { "Rclone UI Manager", options, Box::new(|cc| { + log::debug!("Инициализация шрифтов"); let mut fonts = FontDefinitions::default(); if let Ok(font_data) = std::fs::read("assets/fonts/Sans.otf") { fonts.font_data.insert("sans".to_owned(), FontData::from_owned(font_data)); fonts.families.entry(FontFamily::Proportional).or_default().insert(0, "sans".to_owned()); + log::debug!("Загружен шрифт Sans.otf"); + } else { + log::warn!("Не найден файл шрифта assets/fonts/Sans.otf"); } if let Ok(emoji_data) = std::fs::read("assets/fonts/NotoColorEmoji.ttf") { @@ -33,6 +44,9 @@ fn main() -> Result<(), eframe::Error> { if !prop.contains(&"emoji".to_owned()) { prop.push("emoji".to_owned()); } + log::debug!("Загружен шрифт NotoColorEmoji.ttf"); + } else { + log::warn!("Не найден файл шрифта assets/fonts/NotoColorEmoji.ttf"); } cc.egui_ctx.set_fonts(fonts); diff --git a/src/operations.rs b/src/operations.rs index d9fbfe0..45e15bf 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -24,25 +24,7 @@ pub struct FileInfo { pub modified: Option, } -impl FileInfo { - pub fn format_size(&self) -> String { - let bytes = self.size; - if bytes < 1024 { - format!("{} B", bytes) - } else if bytes < 1024 * 1024 { - format!("{:.1} KB", bytes as f64 / 1024.0) - } else if bytes < 1024 * 1024 * 1024 { - format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0)) - } else { - format!("{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0)) - } - } - - pub fn icon(&self) -> &'static str { - if self.is_dir { "📁" } else { "📄" } - } -} - +pub mod fileinfo; pub mod files; /// === Модуль для операций синхронизации === diff --git a/src/operations/check.rs b/src/operations/check.rs index 423b808..22f8d0c 100644 --- a/src/operations/check.rs +++ b/src/operations/check.rs @@ -1,6 +1,4 @@ -use super::*; use crate::rclone_install::RcloneApp; -use serde_json::Value; /// Проверить разницу между двумя директориями pub fn diff(app: &RcloneApp, source: &str, dest: &str) -> Result, String> { diff --git a/src/operations/fileinfo.rs b/src/operations/fileinfo.rs new file mode 100644 index 0000000..bc42b6e --- /dev/null +++ b/src/operations/fileinfo.rs @@ -0,0 +1,19 @@ +use super::*; +impl FileInfo { + pub fn format_size(&self) -> String { + let bytes = self.size; + if bytes < 1024 { + format!("{} B", bytes) + } else if bytes < 1024 * 1024 { + format!("{:.1} KB", bytes as f64 / 1024.0) + } else if bytes < 1024 * 1024 * 1024 { + format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0)) + } else { + format!("{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0)) + } + } + + pub fn icon(&self) -> &'static str { + if self.is_dir { "📁" } else { "📄" } + } +} diff --git a/src/operations/info.rs b/src/operations/info.rs index 0454b18..bcd5e9f 100644 --- a/src/operations/info.rs +++ b/src/operations/info.rs @@ -1,4 +1,3 @@ -use super::*; use crate::rclone_install::RcloneApp; use serde_json::Value; use std::collections::HashMap; diff --git a/src/operations/local_fs.rs b/src/operations/local_fs.rs index a7f8ab9..6e471cd 100644 --- a/src/operations/local_fs.rs +++ b/src/operations/local_fs.rs @@ -1,11 +1,14 @@ - -use std::fs; -use std::path::{Path, PathBuf}; use super::*; +use std::fs; +use std::path::PathBuf; pub fn list_directory(path: &str) -> Result, String> { let p = if path.is_empty() { - if cfg!(windows) { PathBuf::from("C:\\") } else { PathBuf::from("/") } + if cfg!(windows) { + PathBuf::from("C:\\") + } else { + PathBuf::from("/") + } } else { PathBuf::from(path) }; diff --git a/src/operations/remotes.rs b/src/operations/remotes.rs index f9a5767..571858e 100644 --- a/src/operations/remotes.rs +++ b/src/operations/remotes.rs @@ -4,6 +4,7 @@ use serde_json::Value; /// Получить список всех удаленных хранилищ pub fn list(app: &RcloneApp) -> Result, String> { + log::debug!("Получение списка удаленных хранилищ"); let output = app.run_command(&["listremotes"])?; let mut remotes = Vec::new(); @@ -14,6 +15,7 @@ pub fn list(app: &RcloneApp) -> Result, String> { if let Ok(config) = get_config(app, name) { remotes.push(config); } else { + log::warn!("Не удалось получить конфигурацию для remote '{}'", name); // Если не удалось получить конфиг, добавляем базовую информацию remotes.push(Remote { name: name.to_string(), @@ -24,11 +26,13 @@ pub fn list(app: &RcloneApp) -> Result, String> { } } + log::info!("Найдено {} удаленных хранилищ", remotes.len()); Ok(remotes) } /// Получить конфигурацию конкретного remote pub fn get_config(app: &RcloneApp, remote_name: &str) -> Result { + log::debug!("Получение конфигурации для remote: {}", remote_name); let output = app.run_command(&["config", "dump"])?; let configs: Value = serde_json::from_str(&output) @@ -54,13 +58,16 @@ pub fn get_config(app: &RcloneApp, remote_name: &str) -> Result } } + log::debug!("Получена конфигурация для {} типа {}", remote_name, r#type); Ok(Remote { name: remote_name.to_string(), r#type, config: config_map, }) } else { - Err(format!("Remote '{}' не найден", remote_name)) + let error_msg = format!("Remote '{}' не найден", remote_name); + log::error!("{}", error_msg); + Err(error_msg) } } @@ -71,6 +78,7 @@ pub fn create( r#type: &str, config: &HashMap, ) -> Result<(), String> { + log::info!("Создание нового remote: {} типа {}", name, r#type); let mut args = vec!["config", "create", name, r#type]; for (key, value) in config { @@ -79,11 +87,13 @@ pub fn create( } app.run_command(&args)?; + log::info!("Remote {} успешно создан", name); Ok(()) } /// Обновить конфигурацию remote pub fn update(app: &RcloneApp, name: &str, config: &HashMap) -> Result<(), String> { + log::info!("Обновление конфигурации remote: {}", name); let mut args = vec!["config", "update", name]; for (key, value) in config { @@ -92,24 +102,33 @@ pub fn update(app: &RcloneApp, name: &str, config: &HashMap) -> } app.run_command(&args)?; + log::info!("Конфигурация remote {} успешно обновлена", name); Ok(()) } /// Удалить remote pub fn delete(app: &RcloneApp, name: &str) -> Result<(), String> { + log::info!("Удаление remote: {}", name); app.run_command(&["config", "delete", name])?; + log::info!("Remote {} успешно удален", name); Ok(()) } /// Проверить доступность remote pub fn check(app: &RcloneApp, remote_name: &str) -> Result { + log::debug!("Проверка доступности remote: {}", remote_name); let result = app.run_command(&["lsd", remote_name, "--max-depth", "1"]); match result { - Ok(_) => Ok(true), + Ok(_) => { + log::debug!("Remote {} доступен", remote_name); + Ok(true) + } Err(e) => { if e.contains("directory not found") { + log::warn!("Remote {} существует, но пуст", remote_name); Ok(true) // Remote существует, но пустой } else { + log::error!("Ошибка доступа к remote {}: {}", remote_name, e); Err(e) } } diff --git a/src/operations/sync.rs b/src/operations/sync.rs index 70a8e8d..e01323e 100644 --- a/src/operations/sync.rs +++ b/src/operations/sync.rs @@ -1,6 +1,5 @@ use super::*; use crate::rclone_install::RcloneApp; -use serde_json::Value; /// Скопировать файлы/директории pub fn copy( diff --git a/src/rclone_install/rclone_install_real.rs b/src/rclone_install/rclone_install_real.rs index e487400..8453998 100644 --- a/src/rclone_install/rclone_install_real.rs +++ b/src/rclone_install/rclone_install_real.rs @@ -1,19 +1,18 @@ -use flate2::read::GzDecoder; use reqwest; use std::fs; use std::io::Cursor; use std::path::PathBuf; use std::process::Command; -use tar::Archive; -use tokio; use super::RcloneApp; impl RcloneApp { pub async fn new() -> Result> { + log::info!("Инициализация RcloneApp"); + // Сначала проверяем системный rclone if let Some(system_rclone) = Self::check_system_rclone() { - println!("Найден системный rclone: {:?}", system_rclone); + log::info!("Найден системный rclone: {:?}", system_rclone); return Ok(Self { rclone_path: system_rclone, using_system_rclone: true, @@ -32,7 +31,7 @@ impl RcloneApp { }; if rclone_path.exists() { - println!("Найден rclone в директории приложения: {:?}", rclone_path); + log::info!("Найден rclone в директории приложения: {:?}", rclone_path); return Ok(Self { rclone_path, using_system_rclone: false, @@ -40,12 +39,13 @@ impl RcloneApp { } // Если ничего не найдено - устанавливаем - println!("rclone не найден. Начинаю установку..."); + log::warn!("rclone не найден. Начинаю установку..."); Self::install_latest_rclone().await } // Публичный конструктор для тестов и создания экземпляра с заданными параметрами pub fn new_with_path(rclone_path: PathBuf, using_system_rclone: bool) -> Self { + log::debug!("Создание RcloneApp с путем {:?}, системный: {}", rclone_path, using_system_rclone); Self { rclone_path, using_system_rclone, @@ -63,30 +63,36 @@ impl RcloneApp { // Сеттеры pub fn set_rclone_path(&mut self, path: PathBuf) { + log::debug!("Изменение пути rclone с {:?} на {:?}", self.rclone_path, path); self.rclone_path = path; } pub fn set_using_system_rclone(&mut self, value: bool) { + log::debug!("Изменение флага using_system_rclone с {} на {}", self.using_system_rclone, value); self.using_system_rclone = value; } pub fn check_system_rclone() -> Option { + log::debug!("Проверка системного rclone"); if let Ok(path) = which::which("rclone") { + log::debug!("Найден системный rclone по пути: {:?}", path); return Some(path); } #[cfg(windows)] { if let Ok(path) = which::which("rclone.exe") { + log::debug!("Найден системный rclone.exe по пути: {:?}", path); return Some(path); } } + log::debug!("Системный rclone не найден"); None } async fn install_latest_rclone() -> Result> { - println!("Начинаю установку последней версии rclone..."); + log::info!("Начинаю установку последней версии rclone..."); // Определяем ОС и архитектуру let os = if cfg!(target_os = "linux") { @@ -109,8 +115,12 @@ impl RcloneApp { return Err("Неподдерживаемая архитектура".into()); }; + log::info!("ОС: {}, архитектура: {}", os, arch); + // Получаем информацию о последнем релизе let client = reqwest::Client::new(); + log::debug!("Запрос последней версии rclone с GitHub API"); + let latest_release: serde_json::Value = client .get("https://api.github.com/repos/rclone/rclone/releases/latest") .header("User-Agent", "rclone-ui-installer") @@ -124,27 +134,33 @@ impl RcloneApp { .ok_or("Не удалось получить версию")? .trim_start_matches('v'); + log::info!("Найдена последняя версия rclone: {}", version); + // Формируем URL для скачивания let download_url = format!( "https://github.com/rclone/rclone/releases/download/v{}/rclone-v{}-{}-{}.zip", version, version, os, arch ); - println!("Скачиваю rclone версии {} с: {}", version, download_url); + log::info!("Скачиваю rclone с: {}", download_url); // Скачиваем архив let response = client.get(&download_url).send().await?; if !response.status().is_success() { - return Err(format!("Ошибка скачивания: HTTP {}", response.status()).into()); + let error_msg = format!("Ошибка скачивания: HTTP {}", response.status()); + log::error!("{}", error_msg); + return Err(error_msg.into()); } let bytes = response.bytes().await?; + log::info!("Архив скачан, размер: {} байт", bytes.len()); // Создаем директорию приложения let app_dir = dirs::data_dir() .ok_or("Не удалось найти папку для данных")? .join("rclone-ui"); fs::create_dir_all(&app_dir)?; + log::debug!("Директория приложения: {:?}", app_dir); // Определяем имя исполняемого файла let rclone_filename = if cfg!(windows) { @@ -165,16 +181,18 @@ impl RcloneApp { let file_name = file.name().to_string(); if file_name.ends_with("rclone") || file_name.ends_with("rclone.exe") { + log::debug!("Найден исполняемый файл в архиве: {}", file_name); let mut contents = Vec::new(); std::io::copy(&mut file, &mut contents)?; fs::write(&rclone_path, contents)?; extracted = true; - println!("Найден и распакован: {}", file_name); + log::info!("Распакован: {} в {:?}", file_name, rclone_path); break; } } if !extracted { + log::error!("Не удалось найти исполняемый файл rclone в архиве"); return Err("Не удалось найти исполняемый файл rclone в архиве".into()); } @@ -185,9 +203,10 @@ impl RcloneApp { let mut perms = fs::metadata(&rclone_path)?.permissions(); perms.set_mode(0o755); fs::set_permissions(&rclone_path, perms)?; + log::debug!("Установлены права на выполнение для {:?}", rclone_path); } - println!("rclone успешно установлен в {:?}", rclone_path); + log::info!("rclone успешно установлен в {:?}", rclone_path); // Проверяем установку let output = Command::new(&rclone_path) @@ -197,7 +216,9 @@ impl RcloneApp { if output.status.success() { let version_output = String::from_utf8_lossy(&output.stdout); - println!("Установленная версия rclone:\n{}", version_output); + log::info!("Установленная версия rclone:\n{}", version_output); + } else { + log::warn!("Не удалось проверить версию установленного rclone"); } Ok(Self { @@ -207,19 +228,29 @@ impl RcloneApp { } pub fn run_command(&self, args: &[&str]) -> Result { + log::debug!("Выполнение команды rclone: {} {:?}", self.rclone_path.display(), args); + let output = Command::new(&self.rclone_path) .args(args) .output() - .map_err(|e| format!("Ошибка запуска: {}", e))?; + .map_err(|e| { + log::error!("Ошибка запуска rclone: {}", e); + format!("Ошибка запуска: {}", e) + })?; if output.status.success() { - Ok(String::from_utf8_lossy(&output.stdout).to_string()) + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + log::debug!("Команда выполнена успешно, вывод: {} байт", stdout.len()); + Ok(stdout) } else { - Err(String::from_utf8_lossy(&output.stderr).to_string()) + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + log::error!("Ошибка выполнения команды: {}", stderr); + Err(stderr) } } pub fn version(&self) -> Result { + log::debug!("Получение версии rclone"); self.run_command(&["version"]) } } diff --git a/src/rclone_install/rclone_install_tests.rs b/src/rclone_install/rclone_install_tests.rs index a86ea9e..845cbe1 100644 --- a/src/rclone_install/rclone_install_tests.rs +++ b/src/rclone_install/rclone_install_tests.rs @@ -1,6 +1,5 @@ use super::RcloneApp; use serial_test::serial; -use std::env; use std::fs; use tempfile::tempdir;