diff options
| author | Naz <ndpm13@ch-naseem.com> | 2025-07-29 13:24:35 +0100 |
|---|---|---|
| committer | Naz <ndpm13@ch-naseem.com> | 2025-07-29 13:24:35 +0100 |
| commit | f419a08d2861d76dce3d2a8206d6f1eb24bf1f2e (patch) | |
| tree | 3590c452abdb9c888fd97c08d848f28e271bd6f1 | |
| parent | 4357ff2f74c0b0082741af5e1015ef4da334a430 (diff) | |
🔧refactor: use separate structs for download logic
| -rw-r--r-- | src/main.rs | 21 | ||||
| -rw-r--r-- | src/types.rs | 135 |
2 files changed, 106 insertions, 50 deletions
diff --git a/src/main.rs b/src/main.rs index 4c3d5d7..175b583 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ use clap::Parser; use tokio::fs; -use zap_rs::{AppImage, Cli, Command, Source, SourceMetadata, appimages_dir, index_dir}; +use zap_rs::{ + AppImage, Cli, Command, PackageManager, Source, SourceMetadata, appimages_dir, index_dir, +}; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { @@ -24,23 +26,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { }, }; - if index_dir() - .join(format!("{}.json", &options.executable)) - .exists() - { - eprintln!("{} is already installed.", &options.executable); - } else { - options.download_from_url().await?; - options.save_to_index(&args.appname).await?; - options.create_symlink().await?; - } + PackageManager::install(&options, &args.appname).await?; } Command::Remove(args) => { - let index_file_path = index_dir().join(format!("{}.json", args.appname)); - let index_file_content = fs::read_to_string(&index_file_path).await?; - let appimage: AppImage = serde_json::from_str(&index_file_content)?; - - appimage.remove().await?; + PackageManager::remove(&args.appname).await?; } Command::List => { let mut appimages = fs::read_dir(index_dir()).await?; diff --git a/src/types.rs b/src/types.rs index 3947bfa..e0397a1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -23,33 +23,87 @@ pub struct SourceMetadata { pub url: String, } -impl AppImage { - pub async fn save_to_index(&self, appname: &str) -> Result<(), Box<dyn std::error::Error>> { - fs::create_dir_all(&index_dir()).await?; +#[derive(Debug)] +pub struct PackageManager { + pub downloader: Downloader, + pub index: Index, + pub symlink_manager: SymlinkManager, +} - let index_file = &index_dir().join(format!("{appname}.json")); +#[derive(Debug, Default)] +pub struct Downloader {} - let json = serde_json::to_string_pretty(self)?; - fs::write(index_file, json).await?; +#[derive(Debug, Default)] +pub struct Index {} + +#[derive(Debug, Default)] +pub struct SymlinkManager {} + +impl PackageManager { + pub async fn install( + appimage: &AppImage, + appname: &str, + ) -> Result<(), Box<dyn std::error::Error>> { + if index_dir() + .join(format!("{}.json", &appimage.executable)) + .exists() + { + Err(format!("{} is already installed.", &appimage.executable).into()) + } else { + // Try to extract filename from URL or use default + let url = &appimage.source.meta.url; + let filename = match url.split('/').next_back() { + Some(name) => name.to_string(), + None => format!("{}.AppImage", appimage.executable), + }; + let path = &appimages_dir().join(filename); + + let downloader = crate::Downloader::new(); + downloader.download_with_progress(url, path).await?; + + let index = crate::Index::new(); + index.add(appimage, appname).await?; + + let sm = crate::SymlinkManager::new(); + sm.create(appimage).await?; + Ok(()) + } + } + pub async fn remove(appname: &str) -> Result<(), Box<dyn std::error::Error>> { + let index_file_path = index_dir().join(format!("{appname}.json")); + let index_file_content = fs::read_to_string(&index_file_path).await?; + let appimage: AppImage = serde_json::from_str(&index_file_content)?; + + let home = std::env::var("HOME")?; + let symlink_path = PathBuf::from(home) + .join(".local/bin") + .join(&appimage.executable); + let index_path = index_dir().join(format!("{}.json", &appimage.executable)); + + fs::remove_file(&appimage.file_path).await?; + fs::remove_file(symlink_path).await?; + fs::remove_file(index_path).await?; Ok(()) } - pub async fn download_from_url(&self) -> Result<(), Box<dyn std::error::Error>> { - fs::create_dir_all(&appimages_dir()).await?; +} - // Try to extract filename from URL or use default - let url = &self.source.meta.url; - let filename = match url.split('/').next_back() { - Some(name) => name.to_string(), - None => format!("{}.AppImage", &self.executable), - }; - let file_path = &appimages_dir().join(filename); +impl Downloader { + pub fn new() -> Self { + Self {} + } + pub async fn download_with_progress( + &self, + url: &str, + path: &PathBuf, + ) -> Result<(), Box<dyn std::error::Error>> { + fs::create_dir_all(&appimages_dir()).await?; let resp = reqwest::get(&url.to_string()).await?; let total_size = resp.content_length().unwrap_or(0); let bar = make_progress_bar(total_size); - let mut out = tokio::fs::File::create(&file_path).await?; + let mut out = tokio::fs::File::create(&path).await?; // Stream download with progress updates let mut stream = resp.bytes_stream(); @@ -66,20 +120,46 @@ impl AppImage { #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; - let mut perms = fs::metadata(&file_path).await?.permissions(); + let mut perms = fs::metadata(&path).await?.permissions(); perms.set_mode(0o755); - fs::set_permissions(&file_path, perms).await?; + fs::set_permissions(&path, perms).await?; } Ok(()) } - pub async fn create_symlink(&self) -> Result<(), Box<dyn std::error::Error>> { +} + +impl Index { + pub fn new() -> Self { + Self {} + } + pub async fn add( + &self, + appimage: &AppImage, + appname: &str, + ) -> Result<(), Box<dyn std::error::Error>> { + fs::create_dir_all(&index_dir()).await?; + + let index_file = &index_dir().join(format!("{appname}.json")); + + let json = serde_json::to_string_pretty(appimage)?; + fs::write(index_file, json).await?; + + Ok(()) + } +} + +impl SymlinkManager { + pub fn new() -> Self { + Self {} + } + pub async fn create(&self, appimage: &AppImage) -> Result<(), Box<dyn std::error::Error>> { let home = std::env::var("HOME")?; let local_bin = PathBuf::from(home).join(".local/bin"); fs::create_dir_all(&local_bin).await?; - let symlink_path = local_bin.join(&self.executable); + let symlink_path = local_bin.join(&appimage.executable); #[cfg(unix)] { @@ -89,22 +169,9 @@ impl AppImage { fs::remove_file(&symlink_path).await?; } - std::os::unix::fs::symlink(&self.file_path, &symlink_path)?; + std::os::unix::fs::symlink(&appimage.file_path, &symlink_path)?; } Ok(()) } - pub async fn remove(&self) -> Result<(), Box<dyn std::error::Error>> { - let home = std::env::var("HOME")?; - let symlink_path = PathBuf::from(home) - .join(".local/bin") - .join(&self.executable); - let index_path = index_dir().join(format!("{}.json", &self.executable)); - - fs::remove_file(&self.file_path).await?; - fs::remove_file(symlink_path).await?; - fs::remove_file(index_path).await?; - - Ok(()) - } } |
