rs4j 0.10.2

A small, automatic, efficient, and easy-to-use Rust to Java bridge.
Documentation
use anyhow::Result;
use clap::{Parser, Subcommand};
use rs4j::build::BindgenConfig;
use serde_json::Value;
use std::{env, fs, path::PathBuf, process::Command};

const CURRENT: &str = env!("TARGET");

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
    Compile {
        files: Vec<PathBuf>,

        #[arg(short, long, required = true)]
        pkg: String,

        #[arg(short, long, required = true)]
        out: PathBuf,

        #[arg(short = 'f', long, required = true)]
        out_file: PathBuf,
    },

    Build {
        args: Vec<String>,

        #[arg(short, long)]
        zigbuild: bool,
    },
}

pub fn run_build(args: Vec<String>, zigbuild: bool) -> Result<()> {
    let build_cmd = if zigbuild { "zigbuild" } else { "build" };

    Command::new("cargo")
        .envs(env::vars())
        .arg(build_cmd)
        .args(&args)
        .spawn()?
        .wait()?;

    if !fs::exists("post-build.rs")? {
        return Ok(());
    }

    Command::new("cargo")
        .envs(env::vars())
        .arg("build")
        .arg("--features")
        .arg("post-build")
        .arg("--bin")
        .arg("post-build")
        .spawn()?
        .wait()?;

    let raw = String::from_utf8(
        Command::new("cargo")
            .envs(env::vars())
            .arg("metadata")
            .arg("--no-deps")
            .output()?
            .stdout,
    )?;

    let raw_toml = String::from_utf8(
        Command::new("cargo")
            .envs(env::vars())
            .arg("read-manifest")
            .output()?
            .stdout,
    )?;

    let data = serde_json::from_str::<Value>(&raw)?;
    let data_toml = serde_json::from_str::<Value>(&raw_toml)?;

    let dir = PathBuf::from(data.get("target_directory").unwrap().as_str().unwrap()).join("debug");

    let name = data_toml.get("name").unwrap().as_str().unwrap();

    let mut target = CURRENT.to_string();
    let mut iter = args.iter();

    while let Some(it) = iter.next() {
        if it.to_lowercase() == "--target" {
            if let Some(it) = iter.next() {
                target = it.to_owned();
                break;
            }
        }
    }

    let cwd = env::current_dir()?;
    let exe = dir.join("post-build");
    let fake_dir = dir.join("tmp/fake/dir");

    if !fake_dir.exists() {
        fs::create_dir_all(&fake_dir)?;
    }

    Command::new(exe)
        .envs(env::vars())
        .env("CARGO_MANIFEST_DIR", cwd)
        .env("CARGO_PKG_NAME", name)
        .env("TARGET", target)
        .env("OUT_DIR", fake_dir)
        .spawn()?
        .wait()?;

    Ok(())
}

pub fn main() -> Result<()> {
    let args = Cli::parse();

    match args.command {
        Commands::Compile {
            files,
            pkg,
            out_file,
            out,
        } => {
            BindgenConfig::new()
                .files(files)
                .package(pkg)
                .bindings(out_file)
                .output(out)
                .generate()?;
        }

        Commands::Build { args, zigbuild } => {
            run_build(args, zigbuild)?;
        }
    }

    Ok(())
}