fix: Fix some logic

1) Now code checking lockal rclone before install it
2) fix function to install rclone on device if it doesn`t exists
This commit is contained in:
Black_Raven committed 2026-02-27 17:20:42 +03:00
1 parent a8af4b4abf
commit 5c26c57efe
1 file changed
+125 -251
+125 -251
View File
@@ -13,7 +13,8 @@ pub struct RcloneApp {
} }
impl RcloneApp { impl RcloneApp {
pub fn new() -> Result<Self, Box<dyn std::error::Error>> { pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
// Сначала проверяем системный rclone
if let Some(system_rclone) = Self::check_system_rclone() { if let Some(system_rclone) = Self::check_system_rclone() {
println!("Найден системный rclone: {:?}", system_rclone); println!("Найден системный rclone: {:?}", system_rclone);
return Ok(Self { return Ok(Self {
@@ -21,27 +22,29 @@ impl RcloneApp {
using_system_rclone: true, using_system_rclone: true,
}); });
} }
println!("Системный rclone не найден, использую встроенную версию");
// Проверяем в директории приложения
let app_dir = dirs::data_dir() let app_dir = dirs::data_dir()
.ok_or("Не удалось найти папку для данных")? .ok_or("Не удалось найти папку для данных")?
.join("rclone-ui"); .join("rclone-ui");
fs::create_dir_all(&app_dir)?;
let rclone_path = if cfg!(windows) { let rclone_path = if cfg!(windows) {
app_dir.join("rclone.exe") app_dir.join("rclone.exe")
} else { } else {
app_dir.join("rclone") app_dir.join("rclone")
}; };
/*if !rclone_path.exists() { if rclone_path.exists() {
Self::extract_rclone(&rclone_path)?; println!("Найден rclone в директории приложения: {:?}", rclone_path);
}*/ return Ok(Self {
rclone_path,
using_system_rclone: false,
});
}
Ok(Self { // Если ничего не найдено - устанавливаем
rclone_path, println!("rclone не найден. Начинаю установку...");
using_system_rclone: false, Self::install_latest_rclone().await
})
} }
fn check_system_rclone() -> Option<PathBuf> { fn check_system_rclone() -> Option<PathBuf> {
@@ -59,35 +62,126 @@ impl RcloneApp {
None None
} }
/*fn extract_rclone(dest_path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> { async fn install_latest_rclone() -> Result<Self, Box<dyn std::error::Error>> {
println!("Устанавливаю rclone в {:?}", dest_path); println!("Начинаю установку последней версии rclone...");
let rclone_bytes = unsafe { // Определяем ОС и архитектуру
// unsafe блок, возвращающий значение let os = if cfg!(target_os = "linux") {
if cfg!(windows) { "linux"
include_bytes!("../bin/rclone.exe") } else if cfg!(target_os = "macos") {
} else if cfg!(target_os = "linux") { "osx"
include_bytes!("../bin/rclone") } else if cfg!(target_os = "windows") {
} else if cfg!(target_os = "macos") { "windows"
include_bytes!("../bin/rclone") } else {
} else { return Err("Неподдерживаемая операционная система".into());
panic!("Неподдерживаемая ОС");
}
}; };
fs::write(dest_path, rclone_bytes)?; let arch = if cfg!(target_arch = "x86_64") {
"amd64"
} else if cfg!(target_arch = "aarch64") {
"arm64"
} else if cfg!(target_arch = "arm") {
"arm"
} else {
return Err("Неподдерживаемая архитектура".into());
};
// Получаем информацию о последнем релизе
let client = reqwest::Client::new();
let latest_release: serde_json::Value = client
.get("https://api.github.com/repos/rclone/rclone/releases/latest")
.header("User-Agent", "rclone-ui-installer")
.send()
.await?
.json()
.await?;
let version = latest_release["tag_name"]
.as_str()
.ok_or("Не удалось получить версию")?
.trim_start_matches('v');
// Формируем 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);
// Скачиваем архив
let response = client.get(&download_url).send().await?;
if !response.status().is_success() {
return Err(format!("Ошибка скачивания: HTTP {}", response.status()).into());
}
let bytes = response.bytes().await?;
// Создаем директорию приложения
let app_dir = dirs::data_dir()
.ok_or("Не удалось найти папку для данных")?
.join("rclone-ui");
fs::create_dir_all(&app_dir)?;
// Определяем имя исполняемого файла
let rclone_filename = if cfg!(windows) {
"rclone.exe"
} else {
"rclone"
};
let rclone_path = app_dir.join(rclone_filename);
// Распаковываем архив
use zip::read::ZipArchive;
let cursor = Cursor::new(bytes);
let mut archive = ZipArchive::new(cursor)?;
let mut extracted = false;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let file_name = file.name().to_string();
if file_name.ends_with("rclone") || file_name.ends_with("rclone.exe") {
let mut contents = Vec::new();
std::io::copy(&mut file, &mut contents)?;
fs::write(&rclone_path, contents)?;
extracted = true;
println!("Найден и распакован: {}", file_name);
break;
}
}
if !extracted {
return Err("Не удалось найти исполняемый файл rclone в архиве".into());
}
// Устанавливаем права на выполнение для Unix систем
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(dest_path)?.permissions(); let mut perms = fs::metadata(&rclone_path)?.permissions();
perms.set_mode(0o755); perms.set_mode(0o755);
fs::set_permissions(dest_path, perms)?; fs::set_permissions(&rclone_path, perms)?;
} }
println!("rclone успешно установлен!"); println!("rclone успешно установлен в {:?}", rclone_path);
Ok(())
}*/ // Проверяем установку
let output = Command::new(&rclone_path)
.arg("version")
.output()
.map_err(|e| format!("Ошибка проверки установки: {}", e))?;
if output.status.success() {
let version_output = String::from_utf8_lossy(&output.stdout);
println!("Установленная версия rclone:\n{}", version_output);
}
Ok(Self {
rclone_path,
using_system_rclone: false,
})
}
fn run_command(&self, args: &[&str]) -> Result<String, String> { fn run_command(&self, args: &[&str]) -> Result<String, String> {
let output = Command::new(&self.rclone_path) let output = Command::new(&self.rclone_path)
@@ -101,228 +195,8 @@ impl RcloneApp {
Err(String::from_utf8_lossy(&output.stderr).to_string()) Err(String::from_utf8_lossy(&output.stderr).to_string())
} }
} }
}
/// Асинхронная функция для проверки наличия и версии rclone pub fn version(&self) -> Result<String, String> {
/// Возвращает: self.run_command(&["version"])
/// 0 - rclone установлен и актуален
/// 1 - требуется установка или обновление
pub async fn check_rclone_status_async() -> Result<i32, Box<dyn std::error::Error>> {
if let Some(system_rclone) = RcloneApp::check_system_rclone() {
println!("Найден системный rclone: n{:?}", system_rclone);
let output = Command::new(&system_rclone)
.arg("version")
.output()
.map_err(|e| format!("Ошибка получения версии: {}", e))?;
if output.status.success() {
let version_output = String::from_utf8_lossy(&output.stdout);
println!("Версия rclone: {}", version_output);
if is_version_recent(&version_output) {
return Ok(0);
} else {
println!("Версия rclone устарела, требуется обновление");
return Ok(1);
}
}
}
let app_dir = dirs::data_dir()
.ok_or("Не удалось найти папку для данных")?
.join("rclone-ui");
let rclone_path = if cfg!(windows) {
app_dir.join("rclone.exe")
} else {
app_dir.join("rclone")
};
if rclone_path.exists() {
println!("Найден rclone в директории приложения");
let output = Command::new(&rclone_path)
.arg("version")
.output()
.map_err(|e| format!("Ошибка получения версии: {}", e))?;
if output.status.success() {
let version_output = String::from_utf8_lossy(&output.stdout);
if is_version_recent(&version_output) {
return Ok(0);
} else {
println!("Версия rclone в директории приложения устарела");
return Ok(1);
}
}
}
println!("rclone не найден, требуется установка");
Ok(1)
}
/// Вспомогательная функция для проверки актуальности версии
fn is_version_recent(version_output: &str) -> bool {
!version_output.contains("rclone v1.55")
}
/// Асинхронная функция для установки последней версии rclone из GitHub
pub async fn install_latest_rclone() -> Result<RcloneApp, Box<dyn std::error::Error>> {
println!("Начинаю установку последней версии rclone...");
let os = if cfg!(target_os = "linux") {
"linux"
} else if cfg!(target_os = "macos") {
"osx"
} else if cfg!(target_os = "windows") {
"windows"
} else {
return Err("Неподдерживаемая операционная система".into());
};
let arch = if cfg!(target_arch = "x86_64") {
"amd64"
} else if cfg!(target_arch = "aarch64") {
"arm64"
} else if cfg!(target_arch = "arm") {
"arm"
} else {
return Err("Неподдерживаемая архитектура".into());
};
// Получаем актуальную версию через API GitHub
let client = reqwest::Client::new();
let latest_release: serde_json::Value = client
.get("https://api.github.com/repos/rclone/rclone/releases/latest")
.header("User-Agent", "rclone-ui-installer")
.send()
.await?
.json()
.await?;
let version = latest_release["tag_name"]
.as_str()
.ok_or("Не удалось получить версию")?
.trim_start_matches('v'); // Убираем 'v' из начала версии если есть
// Всегда используем .zip формат для всех ОС
let download_url = format!(
"https://github.com/rclone/rclone/releases/download/v{}/rclone-v{}-{}-{}.zip",
version, version, os, arch
);
println!("Скачиваю rclone с: {}", download_url);
let response = client.get(&download_url).send().await?;
if !response.status().is_success() {
return Err(format!("Ошибка скачивания: HTTP {}", response.status()).into());
}
let bytes = response.bytes().await?;
let app_dir = dirs::data_dir()
.ok_or("Не удалось найти папку для данных")?
.join("rclone-ui");
fs::create_dir_all(&app_dir)?;
// Определяем имя исполняемого файла в зависимости от ОС
let rclone_filename = if cfg!(windows) {
"rclone.exe"
} else {
"rclone"
};
let rclone_path = app_dir.join(rclone_filename);
// Распаковываем zip архив (для всех ОС)
use std::io::Cursor;
use zip::read::ZipArchive;
let cursor = Cursor::new(bytes);
let mut archive = ZipArchive::new(cursor)?;
let mut extracted = false;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let file_name = file.name().to_string(); // Сохраняем имя в отдельную переменную
// Ищем файл rclone или rclone.exe в архиве
if file_name.ends_with("rclone") || file_name.ends_with("rclone.exe") {
// Определяем путь для распаковки
let output_path = if file_name.contains('/') || file_name.contains('\\') {
// Если файл в подпапке, создаем временный путь
let temp_path = app_dir.join("temp_rclone");
fs::create_dir_all(&temp_path)?;
temp_path.join(
file_name
.split(|c| c == '/' || c == '\\')
.last()
.unwrap_or(rclone_filename),
)
} else {
app_dir.join(&file_name)
};
// Распаковываем файл
let mut contents = Vec::new();
std::io::copy(&mut file, &mut contents)?;
fs::write(&output_path, contents)?;
// Перемещаем в нужное место если необходимо
if output_path != rclone_path {
if rclone_path.exists() {
fs::remove_file(&rclone_path)?;
}
fs::rename(output_path, &rclone_path)?;
}
extracted = true;
println!("Найден и распакован: {}", file_name);
break;
}
} }
if !extracted {
return Err("Не удалось найти исполняемый файл rclone в архиве".into());
}
// Удаляем временную папку если она была создана
let temp_path = app_dir.join("temp_rclone");
if temp_path.exists() {
fs::remove_dir_all(temp_path)?;
}
// Устанавливаем права на выполнение для Unix систем
#[cfg(not(windows))]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&rclone_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&rclone_path, perms)?;
}
println!("rclone успешно установлен в {:?}", rclone_path);
// Проверяем установку
let output = Command::new(&rclone_path)
.arg("version")
.output()
.map_err(|e| format!("Ошибка проверки установки: {}", e))?;
if output.status.success() {
let version_output = String::from_utf8_lossy(&output.stdout);
println!("Установленная версия rclone:\n{}", version_output);
} else {
let error = String::from_utf8_lossy(&output.stderr);
return Err(format!("Ошибка при проверке версии rclone: {}", error).into());
}
Ok(RcloneApp {
rclone_path,
using_system_rclone: false,
})
} }