cargo-program 0.0.4

Utility to simplify Gear programs development
Documentation
use std::path::PathBuf;

use anyhow::{Context, Result};
use cargo_metadata::{Metadata, MetadataCommand, Package};

use crate::commands::BuildCommand;
use crate::error::CrateError;

#[derive(Debug, Default)]
pub(crate) struct OutputInfo {
    pub(crate) output_wasm: PathBuf,
    pub(crate) optimized_wasm: PathBuf,
    pub(crate) metadata_wasm: PathBuf,
}

impl OutputInfo {
    pub fn from_command(cmd: &BuildCommand) -> Result<Self> {
        anyhow::ensure!(
            cmd.manifest_path.exists(),
            CrateError::InvalidManifestPath(cmd.manifest_path.clone())
        );

        let mut meta_cmd = MetadataCommand::new();
        let metadata = meta_cmd
            .manifest_path(&cmd.manifest_path)
            .exec()
            .context("unable to invoke `cargo metadata`")?;

        let root_package = Self::root_package(&metadata).ok_or(CrateError::RootPackageNotFound)?;
        let package_name = root_package.name.replace("-", "_");
        let lib_name = root_package
            .targets
            .iter()
            .find(|target| target.kind.iter().any(|t| t == "cdylib"))
            .ok_or(CrateError::LibNameNotFound)?
            .name
            .replace("-", "_");

        let mut target_dir: PathBuf = metadata.target_directory.into();
        let manifest_path = cmd.manifest_path.canonicalize()?;
        let manifest_dir = manifest_path
            .parent()
            .ok_or_else(|| CrateError::InvalidManifestPath(manifest_path.clone()))?;
        let workspace_root = metadata.workspace_root.canonicalize()?;
        if manifest_dir != workspace_root {
            target_dir.push(&package_name);
        }
        target_dir.push("wasm32-unknown-unknown");
        target_dir.push(if cmd.release { "release" } else { "debug" });

        let base_path = target_dir.join(&lib_name);
        let output_wasm = base_path.with_extension("wasm");
        let optimized_wasm = base_path.with_extension("opt.wasm");
        let metadata_wasm = base_path.with_extension("meta.wasm");

        anyhow::ensure!(
            output_wasm.exists(),
            CrateError::OutputNotFound(output_wasm)
        );

        Ok(Self {
            output_wasm,
            optimized_wasm,
            metadata_wasm,
        })
    }

    fn root_package(metadata: &Metadata) -> Option<&Package> {
        let root_id = metadata.resolve.as_ref()?.root.as_ref()?;
        metadata
            .packages
            .iter()
            .find(|package| package.id == *root_id)
    }
}