upstream_rs/services/integration/
appimage_extractor.rs1use crate::services::integration::permission_handler;
2use anyhow::{Result, anyhow};
3use std::fs::{self, File};
4use std::path::{Path, PathBuf};
5use tokio::process::Command;
6
7macro_rules! message {
8 ($cb:expr, $($arg:tt)*) => {{
9 if let Some(cb) = $cb.as_mut() {
10 cb(&format!($($arg)*));
11 }
12 }};
13}
14
15pub struct AppImageExtractor {
16 extract_cache: PathBuf,
17}
18
19impl AppImageExtractor {
20 pub fn new() -> Result<Self> {
21 let temp_path = std::env::temp_dir().join(format!("upstream-{}", std::process::id()));
22 let extract_cache = temp_path.join("appimage_extract");
23 fs::create_dir_all(&extract_cache)?;
24 Ok(Self { extract_cache })
25 }
26
27 pub async fn extract<H>(
30 &self,
31 name: &str,
32 appimage_path: &Path,
33 message_callback: &mut Option<H>,
34 ) -> Result<PathBuf>
35 where
36 H: FnMut(&str),
37 {
38 let extract_path = self.extract_cache.join(name);
39 let squashfs_root = extract_path.join("squashfs-root");
40
41 if squashfs_root.exists() {
43 message!(message_callback, "Using cached extraction for '{}'", name);
44 return Ok(squashfs_root);
45 }
46
47 fs::create_dir_all(&extract_path)?;
48
49 let temp_appimage = extract_path.join("appimage");
50 fs::copy(appimage_path, &temp_appimage)?;
51 permission_handler::make_executable(&temp_appimage)?;
52
53 message!(message_callback, "Extracting AppImage ...");
54
55 let status = Command::new(&temp_appimage)
56 .arg("--appimage-extract")
57 .current_dir(&extract_path)
58 .stdout(File::open("/dev/null")?)
59 .stderr(File::open("/dev/null")?)
60 .status()
61 .await?;
62
63 let _ = fs::remove_file(&temp_appimage);
65
66 if !status.success() {
67 return Err(anyhow!("AppImage extraction failed with status {}", status));
68 }
69
70 if !squashfs_root.exists() {
71 return Err(anyhow!("Extraction completed but squashfs-root not found"));
72 }
73
74 message!(message_callback, "AppImage extracted");
75 Ok(squashfs_root)
76 }
77}
78
79impl Drop for AppImageExtractor {
80 fn drop(&mut self) {
81 let _ = fs::remove_dir_all(&self.extract_cache);
82 }
83}