From 4357ff2f74c0b0082741af5e1015ef4da334a430 Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 10:05:46 +0100 Subject: =?UTF-8?q?=F0=9F=8E=A8style:=20some=20formatting=20here=20and=20t?= =?UTF-8?q?here?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/args.rs | 2 +- src/types.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 03f6b51..39354f6 100644 --- a/src/args.rs +++ b/src/args.rs @@ -30,7 +30,7 @@ pub struct InstallArgs { /// Provide a repository slug, or a direct URL to an appimage. #[arg(long)] pub from: String, - + /// Name of the executable #[arg(long)] pub executable: Option, diff --git a/src/types.rs b/src/types.rs index fef9071..3947bfa 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,5 @@ use futures_util::StreamExt; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use std::path::PathBuf; use tokio::{fs, io::AsyncWriteExt}; @@ -96,7 +96,9 @@ impl AppImage { } pub async fn remove(&self) -> Result<(), Box> { let home = std::env::var("HOME")?; - let symlink_path = PathBuf::from(home).join(".local/bin").join(&self.executable); + 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?; -- cgit v1.2.3 From f419a08d2861d76dce3d2a8206d6f1eb24bf1f2e Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 13:24:35 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20use=20separate=20structs=20for?= =?UTF-8?q?=20download=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.rs | 21 +++------- src/types.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 106 insertions(+), 50 deletions(-) (limited to 'src') 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> { @@ -24,23 +26,10 @@ async fn main() -> Result<(), Box> { }, }; - 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> { - 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> { + 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> { + 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> { - 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> { + 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> { +} + +impl Index { + pub fn new() -> Self { + Self {} + } + pub async fn add( + &self, + appimage: &AppImage, + appname: &str, + ) -> Result<(), Box> { + 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> { 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> { - 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(()) } } -- cgit v1.2.3 From bd8adb9f0193f1482ca866b06cf133ff68fd9a8e Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 13:46:43 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20remove=20types.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appimage.rs | 20 ++++++ src/downloader.rs | 49 +++++++++++++++ src/index.rs | 26 ++++++++ src/lib.rs | 29 ++++----- src/manager.rs | 61 +++++++++++++++++++ src/paths.rs | 14 +++++ src/symlink.rs | 34 +++++++++++ src/types.rs | 177 ------------------------------------------------------ 8 files changed, 216 insertions(+), 194 deletions(-) create mode 100644 src/appimage.rs create mode 100644 src/downloader.rs create mode 100644 src/index.rs create mode 100644 src/manager.rs create mode 100644 src/paths.rs create mode 100644 src/symlink.rs delete mode 100644 src/types.rs (limited to 'src') diff --git a/src/appimage.rs b/src/appimage.rs new file mode 100644 index 0000000..5d0f8e5 --- /dev/null +++ b/src/appimage.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, Serialize, Deserialize)] +pub struct AppImage { + pub file_path: PathBuf, + pub executable: String, + pub source: Source, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Source { + pub identifier: String, + pub meta: SourceMetadata, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SourceMetadata { + pub url: String, +} diff --git a/src/downloader.rs b/src/downloader.rs new file mode 100644 index 0000000..c40b0b4 --- /dev/null +++ b/src/downloader.rs @@ -0,0 +1,49 @@ +use futures_util::StreamExt; +use std::path::PathBuf; +use tokio::{fs, io::AsyncWriteExt}; + +use crate::{appimages_dir, make_progress_bar}; + +#[derive(Debug, Default)] +pub struct Downloader {} + +impl Downloader { + pub fn new() -> Self { + Self {} + } + pub async fn download_with_progress( + &self, + url: &str, + path: &PathBuf, + ) -> Result<(), Box> { + 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(&path).await?; + + // Stream download with progress updates + let mut stream = resp.bytes_stream(); + while let Some(chunk) = stream.next().await { + let chunk = chunk?; + let len = chunk.len() as u64; + out.write_all(&chunk).await?; + bar.inc(len); + } + + bar.finish_with_message("Download complete!"); + + // Make executable + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&path).await?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(&path, perms).await?; + } + + Ok(()) + } +} diff --git a/src/index.rs b/src/index.rs new file mode 100644 index 0000000..e543b1d --- /dev/null +++ b/src/index.rs @@ -0,0 +1,26 @@ +use tokio::fs; + +use crate::{AppImage, index_dir}; + +#[derive(Debug, Default)] +pub struct Index {} + +impl Index { + pub fn new() -> Self { + Self {} + } + pub async fn add( + &self, + appimage: &AppImage, + appname: &str, + ) -> Result<(), Box> { + 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(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index e09a021..c0db44a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,22 +1,17 @@ +mod appimage; mod args; +mod downloader; +mod index; +mod manager; +mod paths; +mod symlink; mod tui; -mod types; +pub use crate::appimage::*; pub use crate::args::*; +pub use crate::downloader::*; +pub use crate::index::*; +pub use crate::manager::*; +pub use crate::paths::*; +pub use crate::symlink::*; pub use crate::tui::*; -pub use crate::types::*; - -use std::path::PathBuf; - -pub fn zap_rs_home() -> PathBuf { - let home = std::env::var("HOME").expect("HOME not set"); - PathBuf::from(home).join(".local/share/zap-rs") -} - -pub fn index_dir() -> PathBuf { - zap_rs_home().join("index") -} - -pub fn appimages_dir() -> PathBuf { - zap_rs_home().join("appimages") -} diff --git a/src/manager.rs b/src/manager.rs new file mode 100644 index 0000000..e810bda --- /dev/null +++ b/src/manager.rs @@ -0,0 +1,61 @@ +use std::path::PathBuf; + +use tokio::fs; + +use crate::{AppImage, Downloader, Index, SymlinkManager, appimages_dir, index_dir}; + +#[derive(Debug)] +pub struct PackageManager { + pub downloader: Downloader, + pub index: Index, + pub symlink_manager: SymlinkManager, +} + +impl PackageManager { + pub async fn install( + appimage: &AppImage, + appname: &str, + ) -> Result<(), Box> { + 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> { + 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(()) + } +} diff --git a/src/paths.rs b/src/paths.rs new file mode 100644 index 0000000..172cae6 --- /dev/null +++ b/src/paths.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; + +pub fn zap_rs_home() -> PathBuf { + let home = std::env::var("HOME").expect("HOME not set"); + PathBuf::from(home).join(".local/share/zap-rs") +} + +pub fn index_dir() -> PathBuf { + zap_rs_home().join("index") +} + +pub fn appimages_dir() -> PathBuf { + zap_rs_home().join("appimages") +} diff --git a/src/symlink.rs b/src/symlink.rs new file mode 100644 index 0000000..3883172 --- /dev/null +++ b/src/symlink.rs @@ -0,0 +1,34 @@ +use std::path::PathBuf; +use tokio::fs; + +use crate::AppImage; + +#[derive(Debug, Default)] +pub struct SymlinkManager {} + +impl SymlinkManager { + pub fn new() -> Self { + Self {} + } + pub async fn create(&self, appimage: &AppImage) -> Result<(), Box> { + 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(&appimage.executable); + + #[cfg(unix)] + { + use tokio::fs; + + if symlink_path.exists() { + fs::remove_file(&symlink_path).await?; + } + + std::os::unix::fs::symlink(&appimage.file_path, &symlink_path)?; + } + + Ok(()) + } +} diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index e0397a1..0000000 --- a/src/types.rs +++ /dev/null @@ -1,177 +0,0 @@ -use futures_util::StreamExt; -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; -use tokio::{fs, io::AsyncWriteExt}; - -use crate::{appimages_dir, index_dir, make_progress_bar}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct AppImage { - pub file_path: PathBuf, - pub executable: String, - pub source: Source, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Source { - pub identifier: String, - pub meta: SourceMetadata, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SourceMetadata { - pub url: String, -} - -#[derive(Debug)] -pub struct PackageManager { - pub downloader: Downloader, - pub index: Index, - pub symlink_manager: SymlinkManager, -} - -#[derive(Debug, Default)] -pub struct Downloader {} - -#[derive(Debug, Default)] -pub struct Index {} - -#[derive(Debug, Default)] -pub struct SymlinkManager {} - -impl PackageManager { - pub async fn install( - appimage: &AppImage, - appname: &str, - ) -> Result<(), Box> { - 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> { - 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(()) - } -} - -impl Downloader { - pub fn new() -> Self { - Self {} - } - pub async fn download_with_progress( - &self, - url: &str, - path: &PathBuf, - ) -> Result<(), Box> { - 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(&path).await?; - - // Stream download with progress updates - let mut stream = resp.bytes_stream(); - while let Some(chunk) = stream.next().await { - let chunk = chunk?; - let len = chunk.len() as u64; - out.write_all(&chunk).await?; - bar.inc(len); - } - - bar.finish_with_message("Download complete!"); - - // Make executable - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let mut perms = fs::metadata(&path).await?.permissions(); - perms.set_mode(0o755); - fs::set_permissions(&path, perms).await?; - } - - Ok(()) - } -} - -impl Index { - pub fn new() -> Self { - Self {} - } - pub async fn add( - &self, - appimage: &AppImage, - appname: &str, - ) -> Result<(), Box> { - 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> { - 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(&appimage.executable); - - #[cfg(unix)] - { - use tokio::fs; - - if symlink_path.exists() { - fs::remove_file(&symlink_path).await?; - } - - std::os::unix::fs::symlink(&appimage.file_path, &symlink_path)?; - } - - Ok(()) - } -} -- cgit v1.2.3 From cd49bc9362c0fef2534948d368c8c52460558a77 Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 16:26:28 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20move=20the=20path=20preparing?= =?UTF-8?q?=20logic=20to=20Downloader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/downloader.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/downloader.rs b/src/downloader.rs index c40b0b4..2196e25 100644 --- a/src/downloader.rs +++ b/src/downloader.rs @@ -11,6 +11,15 @@ impl Downloader { pub fn new() -> Self { Self {} } + pub fn prepare_path(&self, url: &str, executable: &str) -> PathBuf { + // Try to extract filename from URL or use default + let filename = match url.split('/').next_back() { + Some(name) => name.to_string(), + None => format!("{executable}.AppImage"), + }; + + appimages_dir().join(filename) + } pub async fn download_with_progress( &self, url: &str, -- cgit v1.2.3 From 2cef21906b9bd3c2e92925d7c93d3c8053b52ade Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 16:27:18 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20move=20the=20rest=20of=20the=20?= =?UTF-8?q?indexing=20relating=20logic=20to=20Index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src') diff --git a/src/index.rs b/src/index.rs index e543b1d..069068a 100644 --- a/src/index.rs +++ b/src/index.rs @@ -9,6 +9,16 @@ impl Index { pub fn new() -> Self { Self {} } + pub async fn get(&self, appname: &str) -> Result> { + 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)?; + + Ok(appimage) + } + pub fn exists(&self, executable: &str) -> bool { + index_dir().join(format!("{}.json", &executable)).exists() + } pub async fn add( &self, appimage: &AppImage, @@ -21,6 +31,12 @@ impl Index { let json = serde_json::to_string_pretty(appimage)?; fs::write(index_file, json).await?; + Ok(()) + } + pub async fn remove(&self, appname: &str) -> Result<(), Box> { + let index_file_path = index_dir().join(format!("{appname}.json")); + fs::remove_file(index_file_path).await?; + Ok(()) } } -- cgit v1.2.3 From 53cbca7ae6e3c1e37e59cafcd45efdba65c732ed Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 16:28:14 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20move=20the=20symlink=20removal?= =?UTF-8?q?=20logic=20to=20SymlinkManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/symlink.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/symlink.rs b/src/symlink.rs index 3883172..843115c 100644 --- a/src/symlink.rs +++ b/src/symlink.rs @@ -10,6 +10,14 @@ impl SymlinkManager { pub fn new() -> Self { Self {} } + pub async fn remove(&self, executable: &str) -> Result<(), Box> { + let home = std::env::var("HOME")?; + let symlink_path = PathBuf::from(home).join(".local/bin").join(executable); + + fs::remove_file(symlink_path).await?; + + Ok(()) + } pub async fn create(&self, appimage: &AppImage) -> Result<(), Box> { let home = std::env::var("HOME")?; let local_bin = PathBuf::from(home).join(".local/bin"); -- cgit v1.2.3 From b5014598543533403fb11b570345790ff06d0cce Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 16:29:50 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20replace=20old=20logic=20with=20?= =?UTF-8?q?new=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.rs | 22 ++++++++------------ src/manager.rs | 66 ++++++++++++++++++++++++---------------------------------- 2 files changed, 36 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 175b583..f1f6bbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ +use std::path::PathBuf; + use clap::Parser; use tokio::fs; -use zap_rs::{ - AppImage, Cli, Command, PackageManager, Source, SourceMetadata, appimages_dir, index_dir, -}; +use zap_rs::{AppImage, Cli, Command, PackageManager, Source, SourceMetadata, index_dir}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -11,14 +11,8 @@ async fn main() -> Result<(), Box> { match args.command { Command::Install(args) => { - let options = AppImage { - file_path: appimages_dir().join( - args.from - .split('/') - .next_back() - .filter(|s| !s.is_empty()) - .unwrap_or("app.AppImage"), - ), + let mut options = AppImage { + file_path: PathBuf::new(), executable: args.executable.unwrap_or(args.appname.clone()), source: Source { identifier: "raw_url".to_string(), @@ -26,10 +20,12 @@ async fn main() -> Result<(), Box> { }, }; - PackageManager::install(&options, &args.appname).await?; + let pm = PackageManager::new(); + pm.install(&mut options, &args.appname).await?; } Command::Remove(args) => { - PackageManager::remove(&args.appname).await?; + let pm = PackageManager::new(); + pm.remove(&args.appname).await?; } Command::List => { let mut appimages = fs::read_dir(index_dir()).await?; diff --git a/src/manager.rs b/src/manager.rs index e810bda..9257fa7 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,10 +1,8 @@ -use std::path::PathBuf; - use tokio::fs; -use crate::{AppImage, Downloader, Index, SymlinkManager, appimages_dir, index_dir}; +use crate::{AppImage, Downloader, Index, SymlinkManager}; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct PackageManager { pub downloader: Downloader, pub index: Index, @@ -12,49 +10,39 @@ pub struct PackageManager { } impl PackageManager { + pub fn new() -> Self { + Self { + downloader: Downloader::new(), + index: Index::new(), + symlink_manager: SymlinkManager::new(), + } + } pub async fn install( - appimage: &AppImage, + &self, + appimage: &mut AppImage, appname: &str, ) -> Result<(), Box> { - 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?; + if self.index.exists(&appimage.executable) { + return Err(format!("{} is already installed.", &appimage.executable).into()); + } - let index = crate::Index::new(); - index.add(appimage, appname).await?; + appimage.file_path = self + .downloader + .prepare_path(&appimage.source.meta.url, &appimage.executable); + self.downloader + .download_with_progress(&appimage.source.meta.url, &appimage.file_path) + .await?; - let sm = crate::SymlinkManager::new(); - sm.create(appimage).await?; - Ok(()) - } + self.index.add(appimage, appname).await?; + self.symlink_manager.create(appimage).await?; + Ok(()) } - pub async fn remove(appname: &str) -> Result<(), Box> { - 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)); + pub async fn remove(&self, appname: &str) -> Result<(), Box> { + let appimage = self.index.get(appname).await?; fs::remove_file(&appimage.file_path).await?; - fs::remove_file(symlink_path).await?; - fs::remove_file(index_path).await?; + self.symlink_manager.remove(&appimage.executable).await?; + self.index.remove(appname).await?; Ok(()) } -- cgit v1.2.3 From 42fa16e58b8a1563650c54bf9eb55e7e9dcef11b Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 29 Jul 2025 16:58:08 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20use=20a=20single=20PackageManag?= =?UTF-8?q?er=20instance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index f1f6bbd..2b588e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use zap_rs::{AppImage, Cli, Command, PackageManager, Source, SourceMetadata, ind #[tokio::main] async fn main() -> Result<(), Box> { let args = Cli::parse(); + let pm = PackageManager::new(); match args.command { Command::Install(args) => { @@ -20,11 +21,9 @@ async fn main() -> Result<(), Box> { }, }; - let pm = PackageManager::new(); pm.install(&mut options, &args.appname).await?; } Command::Remove(args) => { - let pm = PackageManager::new(); pm.remove(&args.appname).await?; } Command::List => { -- cgit v1.2.3 From 11a86042e73bb0eecad61ac6e636dd98563167f5 Mon Sep 17 00:00:00 2001 From: Naz Date: Wed, 30 Jul 2025 07:09:37 +0100 Subject: =?UTF-8?q?=F0=9F=94=A7refactor:=20move=20the=20list=20logic=20to?= =?UTF-8?q?=20PackageManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.rs | 11 ++--------- src/manager.rs | 13 ++++++++++++- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 2b588e0..1d9505b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ use std::path::PathBuf; use clap::Parser; -use tokio::fs; -use zap_rs::{AppImage, Cli, Command, PackageManager, Source, SourceMetadata, index_dir}; +use zap_rs::{AppImage, Cli, Command, PackageManager, Source, SourceMetadata}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -27,13 +26,7 @@ async fn main() -> Result<(), Box> { pm.remove(&args.appname).await?; } Command::List => { - let mut appimages = fs::read_dir(index_dir()).await?; - - while let Some(appimage) = appimages.next_entry().await? { - if let Some(name) = appimage.file_name().to_str() { - println!("- {}", name.strip_suffix(".json").unwrap()); - } - } + pm.list().await?; } }; diff --git a/src/manager.rs b/src/manager.rs index 9257fa7..2c75900 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,6 +1,6 @@ use tokio::fs; -use crate::{AppImage, Downloader, Index, SymlinkManager}; +use crate::{AppImage, Downloader, Index, SymlinkManager, index_dir}; #[derive(Debug, Default)] pub struct PackageManager { @@ -44,6 +44,17 @@ impl PackageManager { self.symlink_manager.remove(&appimage.executable).await?; self.index.remove(appname).await?; + Ok(()) + } + pub async fn list(&self) -> Result<(), Box> { + let mut appimages = fs::read_dir(index_dir()).await?; + + while let Some(appimage) = appimages.next_entry().await? { + if let Some(name) = appimage.file_name().to_str() { + println!("- {}", name.strip_suffix(".json").unwrap()); + } + } + Ok(()) } } -- cgit v1.2.3