cargo-flutter 0.1.7

Tool for building flutter-rs projects.
Documentation
use crate::cargo::Cargo;
use crate::package::Package;
use serde::Deserialize;
use std::fs::Permissions;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::process::Command;

#[derive(Debug, Default, Clone, Deserialize)]
pub struct TomlAppImage {
    name: Option<String>,
    icon: Option<String>,
}

pub struct AppImage {
    toml: TomlAppImage,
}

impl AppImage {
    pub fn new(toml: TomlAppImage) -> Self {
        Self { toml }
    }

    #[cfg(not(unix))]
    pub fn build(
        &self,
        cargo: &Cargo,
        package: &Package,
        sign: bool,
    ) -> Result<(), Box<dyn std::error::Error>> {
        Err(failure::format_err!("Creating appimages only supported from a unix host.").into())
    }

    #[cfg(unix)]
    pub fn build(
        &self,
        cargo: &Cargo,
        package: &Package,
        sign: bool,
    ) -> Result<(), Box<dyn std::error::Error>> {
        let build_dir = cargo.build_dir();
        let appimage_dir = build_dir.join("appimage");
        let name = self.toml.name.as_ref().unwrap_or(&package.name);
        let exec = &package.name;
        let icon_path = self
            .toml
            .icon
            .as_ref()
            .map(PathBuf::from)
            .unwrap_or_else(|| cargo.workspace().root().join("assets").join("icon.svg"));
        if !icon_path.exists() {
            return Err(failure::format_err!("Icon not found {}", icon_path.display()).into());
        }
        let icon = icon_path
            .file_stem()
            .map(|f| f.to_str().unwrap())
            .unwrap_or("icon")
            .to_string();
        std::fs::remove_dir_all(&appimage_dir).ok();

        let bin_dir = appimage_dir.join("usr").join("bin");
        std::fs::create_dir_all(&bin_dir)?;
        for bin in package.bins() {
            std::fs::copy(bin.path(), bin_dir.join(bin.name()))?;
        }

        let lib_dir = appimage_dir.join("usr").join("lib");
        std::fs::create_dir_all(&lib_dir)?;
        for lib in package.libs() {
            std::fs::copy(lib.path(), lib_dir.join(lib.name()))?;
        }

        let asset_dir = appimage_dir.join("usr").join("share");
        std::fs::create_dir_all(&asset_dir)?;
        for asset in package.assets() {
            copy_dir::copy_dir(asset.path(), asset_dir.join(asset.name()))?;
        }

        let apprun = appimage_dir.join("AppRun");
        std::fs::write(&apprun, APP_RUN)?;
        std::fs::set_permissions(&apprun, Permissions::from_mode(0o755))?;

        let desktop = appimage_dir.join(format!("{}.desktop", exec));
        std::fs::write(&desktop, gen_desktop(name, exec, &icon))?;
        std::fs::set_permissions(&desktop, Permissions::from_mode(0o755))?;

        std::fs::copy(
            &icon_path,
            appimage_dir.join(icon_path.file_name().unwrap()),
        )?;

        let mut cmd = Command::new("appimagetool");
        cmd.current_dir(&build_dir).arg("appimage");
        if sign {
            cmd.arg("--sign");
        }
        cmd.status().expect("Success");

        Ok(())
    }
}

const APP_RUN: &str = r#"#!/bin/sh
SELF=$(readlink -f "$0")
HERE=${SELF%/*}
export PATH="${HERE}/usr/bin/${PATH:+:$PATH}"
export LD_LIBRARY_PATH="${HERE}/usr/lib/:${LD_LIBRARY_PATH:+:$LDLIBRARY_PATH}"
export FLUTTER_ASSET_DIR="${HERE}/usr/share/flutter_assets"
export FLUTTER_AOT_SNAPSHOT="${HERE}/usr/lib/app.so"
EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
exec "${EXEC}" "$@"
"#;

fn gen_desktop(name: &str, exec: &str, icon: &str) -> String {
    format!(
        r#"[Desktop Entry]
Name={}
Exec={}
Icon={}
Type=Application
Categories=Utility;
"#,
        name, exec, icon
    )
}