webapp-rs 0.1.0

A simple CLI tool to create webapps (only support firefox and linux for now.
use crate::cli::EditArgs;
use anyhow::{Context, Result};
use std::path::Path;

pub struct DesktopFile;

impl DesktopFile {
    /// Crée un nouveau fichier desktop pour une webapp
    #[allow(clippy::too_many_arguments)]
    pub fn create(
        path: &Path,
        name: &str,
        url: &str,
        icon: &str,
        category: &str,
        codename: &str,
        exec: &str,
        private_window: bool,
    ) -> Result<()> {
        let content = format!(
            r#"[Desktop Entry]
Version=1.0
Name={name}
Comment=Web App
Exec={exec}
Terminal=false
Type=Application
Icon={icon}
Categories=GTK;{category};
MimeType=text/html;text/xml;application/xhtml_xml;
StartupWMClass=WebApp-{codename}
StartupNotify=true
X-WebApp-Browser=Firefox
X-WebApp-URL={url}
X-WebApp-PrivateWindow={private_window}
X-WebApp-Isolated=true
"#,
            private_window = if private_window { "true" } else { "false" },
        );

        std::fs::write(path, content).context("Failed to write desktop file")?;

        // Rendre le fichier exécutable
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let mut perms = std::fs::metadata(path)?.permissions();
            perms.set_mode(0o644);
            std::fs::set_permissions(path, perms)?;
        }

        Ok(())
    }

    /// Édite un fichier desktop existant
    pub fn edit(path: &Path, args: EditArgs) -> Result<()> {
        let content = std::fs::read_to_string(path).context("Failed to read desktop file")?;

        let mut lines: Vec<String> = content.lines().map(|l| l.to_string()).collect();

        if let Some(name) = args.name {
            Self::replace_line(&mut lines, "Name", &name);
        }

        if let Some(url) = args.url {
            Self::replace_line(&mut lines, "X-WebApp-URL", &url);
        }

        if let Some(icon) = args.icon {
            Self::replace_line(&mut lines, "Icon", &icon);
        }

        if let Some(private_window) = args.private_window {
            let value = if private_window { "true" } else { "false" };
            Self::replace_line(&mut lines, "X-WebApp-PrivateWindow", value);
        }

        std::fs::write(path, lines.join("\n")).context("Failed to write desktop file")?;

        Ok(())
    }

    /// Remplace une ligne dans le fichier desktop
    fn replace_line(lines: &mut [String], prefix: &str, value: &str) {
        for line in lines.iter_mut() {
            if line.starts_with(&format!("{}=", prefix)) {
                *line = format!("{}={}", prefix, value);
                return;
            }
        }
    }

    /// Supprime un fichier desktop
    pub fn delete(path: &Path) -> Result<()> {
        if path.exists() {
            std::fs::remove_file(path).context("Failed to delete desktop file")?;
        }
        Ok(())
    }

    /// Lit et exécute la commande d'un fichier desktop
    pub fn get_exec_command(path: &Path) -> Result<String> {
        let content = std::fs::read_to_string(path).context("Failed to read desktop file")?;

        let exec_line = content
            .lines()
            .find(|line| line.starts_with("Exec="))
            .ok_or_else(|| anyhow::anyhow!("No Exec line found in desktop file"))?;

        Ok(exec_line.strip_prefix("Exec=").unwrap_or("").to_string())
    }
}