summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNaz <ndpm13@ch-naseem.com>2025-08-08 20:02:55 +0100
committerNaz <ndpm13@ch-naseem.com>2025-08-08 20:02:55 +0100
commit4c28c8e00cb6e31f5ab1970cadf521edecfe6f58 (patch)
treea747865f84c549c13a77e93c8a0801fb48f0d2e2
parent7d6dc364dd6c1a1f8d200eaf108e8c56729b17ae (diff)
parente961fad84734ba750386ed463057bacedf24bc17 (diff)
Merge pull request '✨feat: add desktop integration logic to AppImage and utilize it in PackageManager' (#18) from feat/issue-17 into main
Reviewed-on: https://git.ch-naseem.com/ndpm13/zap-rs/pulls/18
-rw-r--r--Cargo.lock12
-rw-r--r--README.md2
-rw-r--r--src/appimage.rs133
-rw-r--r--src/main.rs18
-rw-r--r--src/manager.rs38
-rw-r--r--src/paths.rs8
6 files changed, 187 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7ee3b05..e33e701 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -152,9 +152,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
-version = "1.2.31"
+version = "1.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
+checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
dependencies = [
"shlex",
]
@@ -564,9 +564,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.15.4"
+version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]]
name = "heck"
@@ -1507,9 +1507,9 @@ dependencies = [
[[package]]
name = "slab"
-version = "0.4.10"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
diff --git a/README.md b/README.md
index a7db689..56b523d 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ AppImage package manager inspired by [zap](https://github.com/srevinsaju/zap), b
## Install
```bash
-cargo install --git https://github.com/ndpm13/zap-rs
+cargo install zap-rs
```
## Usage
diff --git a/src/appimage.rs b/src/appimage.rs
index 5d0f8e5..bf0de6d 100644
--- a/src/appimage.rs
+++ b/src/appimage.rs
@@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
+use std::{path::PathBuf, process::Command};
+use tokio::fs;
+
+use crate::{Error, InstallArgs, Result, desktops_dir, icons_dir};
#[derive(Debug, Serialize, Deserialize)]
pub struct AppImage {
@@ -18,3 +21,131 @@ pub struct Source {
pub struct SourceMetadata {
pub url: String,
}
+
+impl AppImage {
+ pub fn new(options: &InstallArgs) -> Self {
+ Self {
+ file_path: PathBuf::new(),
+ executable: options
+ .executable
+ .as_ref()
+ .unwrap_or(&options.appname)
+ .to_string(),
+ source: Source {
+ identifier: if options.github {
+ "git.github".to_string()
+ } else {
+ "raw_url".to_string()
+ },
+ meta: SourceMetadata {
+ url: options.from.clone(),
+ },
+ },
+ }
+ }
+ async fn extract_assets(&self) -> Result<PathBuf> {
+ let temp_dir = std::env::temp_dir().join("zap-rs");
+
+ fs::create_dir_all(&temp_dir).await?;
+
+ // Extract desktop file
+ Command::new(&self.file_path)
+ .arg("--appimage-extract")
+ .arg("*.desktop")
+ .current_dir(&temp_dir)
+ .spawn()?
+ .wait()?;
+
+ // Extract icon
+ Command::new(&self.file_path)
+ .arg("--appimage-extract")
+ .arg("usr/share/icons/hicolor/512x512/apps/*.png")
+ .current_dir(&temp_dir)
+ .spawn()?
+ .wait()?;
+
+ Ok(temp_dir)
+ }
+ async fn fix_desktop(&self, desktop_file_path: &PathBuf) -> Result<()> {
+ let file_content = fs::read_to_string(&desktop_file_path).await?;
+
+ let appimage_path = self.file_path.to_str().ok_or(Error::InvalidPath)?;
+
+ let icon_path = icons_dir()?
+ .join(format!("{}.png", self.executable))
+ .to_str()
+ .ok_or(Error::InvalidPath)?
+ .to_string();
+
+ let fixed_file_content: Vec<String> = file_content
+ .lines()
+ .map(|line| {
+ if line.contains("Exec=") {
+ if let Some(exec_line) = line.split_once(" ") {
+ if let Some(exec_arg) = exec_line.0.split_once("=") {
+ format!("{}={} {}", exec_arg.0, appimage_path, exec_line.1)
+ } else {
+ line.to_string()
+ }
+ } else if let Some(exec_arg) = line.split_once("=") {
+ format!("{}={}", exec_arg.0, appimage_path)
+ } else {
+ line.to_string()
+ }
+ } else if line.contains("Icon=") {
+ if let Some(exec_arg) = line.split_once("=") {
+ format!("{}={}", exec_arg.0, icon_path)
+ } else {
+ line.to_string()
+ }
+ } else {
+ line.to_string()
+ }
+ })
+ .collect();
+
+ fs::write(desktop_file_path, fixed_file_content.join("\n")).await?;
+
+ Ok(())
+ }
+ pub async fn integrate_desktop(&self) -> Result<()> {
+ let temp_dir = self.extract_assets().await?;
+ let squashfs = &temp_dir.join("squashfs-root");
+
+ fs::create_dir_all(desktops_dir()?).await?;
+ fs::create_dir_all(icons_dir()?).await?;
+
+ let icon_path = icons_dir()?.join(format!("{}.png", self.executable));
+ let desktop_file_paths = (
+ desktops_dir()?.join(format!("{}.desktop", self.executable)),
+ PathBuf::from(std::env::var("HOME")?).join(format!(
+ ".local/share/applications/{}.desktop",
+ self.executable
+ )),
+ );
+
+ let mut squashfs_entries = fs::read_dir(&squashfs).await?;
+ while let Some(entry) = squashfs_entries.next_entry().await? {
+ if entry.path().extension() == Some("desktop".as_ref()) {
+ fs::copy(entry.path(), &desktop_file_paths.0).await?;
+
+ self.fix_desktop(&desktop_file_paths.0).await?;
+
+ fs::copy(&desktop_file_paths.0, &desktop_file_paths.1).await?;
+ }
+ }
+
+ let mut squashfs_icon_entries =
+ fs::read_dir(&squashfs.join("usr/share/icons/hicolor/512x512/apps")).await?;
+ while let Some(entry) = squashfs_icon_entries.next_entry().await? {
+ if entry.path().extension() == Some("png".as_ref()) {
+ fs::copy(entry.path(), &icon_path).await?;
+ }
+ }
+
+ // Clean up
+ fs::remove_dir_all(temp_dir).await?;
+
+ Ok(())
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index e4bc296..3081598 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,7 @@
use clap::Parser;
use colored::Colorize;
-use std::path::PathBuf;
-use zap_rs::{AppImage, Cli, Command, PackageManager, Result, Source, SourceMetadata};
+use zap_rs::{AppImage, Cli, Command, PackageManager, Result};
async fn run() -> Result<()> {
let args = Cli::parse();
@@ -10,20 +9,9 @@ async fn run() -> Result<()> {
match args.command {
Command::Install(args) => {
- let mut options = AppImage {
- file_path: PathBuf::new(),
- executable: args.executable.unwrap_or(args.appname.clone()),
- source: Source {
- identifier: if args.github {
- "git.github".to_string()
- } else {
- "raw_url".to_string()
- },
- meta: SourceMetadata { url: args.from },
- },
- };
+ let mut appimage = AppImage::new(&args);
- pm.install(&mut options, &args.appname).await?;
+ pm.install(&mut appimage, &args.appname).await?;
}
Command::Remove(args) => {
pm.remove(&args.appname).await?;
diff --git a/src/manager.rs b/src/manager.rs
index 15f28d9..bdb152a 100644
--- a/src/manager.rs
+++ b/src/manager.rs
@@ -1,7 +1,12 @@
+use std::{
+ io::{self, Write},
+ path::PathBuf,
+};
use tokio::fs;
use crate::{
- AppImage, Downloader, Index, Result, SymlinkManager, get_github_release_url, index_dir,
+ AppImage, Downloader, Index, Result, SymlinkManager, desktops_dir, get_github_release_url,
+ icons_dir, index_dir,
};
#[derive(Debug, Default)]
@@ -44,6 +49,17 @@ impl PackageManager {
self.index.add(appimage, appname).await?;
self.symlink_manager.create(appimage).await?;
+
+ print!("Do you want to integrate this appimage? (y/N) ");
+ io::stdout().flush()?;
+
+ let mut input = String::new();
+ io::stdin().read_line(&mut input)?;
+
+ if input.to_lowercase().trim() == "y" || input.to_lowercase().trim() == "yes" {
+ appimage.integrate_desktop().await?;
+ }
+
Ok(())
}
pub async fn remove(&self, appname: &str) -> Result<()> {
@@ -53,6 +69,26 @@ impl PackageManager {
self.symlink_manager.remove(&appimage.executable).await?;
self.index.remove(appname).await?;
+ if fs::try_exists(desktops_dir()?.join(format!("{}.desktop", appimage.executable))).await? {
+ fs::remove_file(desktops_dir()?.join(format!("{}.desktop", appimage.executable)))
+ .await?;
+ }
+ if fs::try_exists(PathBuf::from(std::env::var("HOME")?).join(format!(
+ ".local/share/applications/{}.desktop",
+ appimage.executable
+ )))
+ .await?
+ {
+ fs::remove_file(PathBuf::from(std::env::var("HOME")?).join(format!(
+ ".local/share/applications/{}.desktop",
+ appimage.executable
+ )))
+ .await?;
+ }
+ if fs::try_exists(icons_dir()?.join(format!("{}.png", appimage.executable))).await? {
+ fs::remove_file(icons_dir()?.join(format!("{}.png", appimage.executable))).await?;
+ }
+
Ok(())
}
pub async fn list(&self) -> Result<()> {
diff --git a/src/paths.rs b/src/paths.rs
index 7bbc8f8..c52a84e 100644
--- a/src/paths.rs
+++ b/src/paths.rs
@@ -14,3 +14,11 @@ pub fn index_dir() -> Result<PathBuf> {
pub fn appimages_dir() -> Result<PathBuf> {
Ok(zap_rs_home()?.join("appimages"))
}
+
+pub fn desktops_dir() -> Result<PathBuf> {
+ Ok(zap_rs_home()?.join("desktops"))
+}
+
+pub fn icons_dir() -> Result<PathBuf> {
+ Ok(zap_rs_home()?.join("icons"))
+}