jam-pvm-build 0.1.28

CLI utility for building PVM code blobs, particularly services and authorizers
use clap::Parser;
use jam_pvm_builder::{BlobType, ProfileType};
use temp_dir::TempDir;

#[derive(clap::ValueEnum, Clone, Debug)]
#[value(rename_all = "lowercase")]
enum ModuleType {
	/// Automatically derive the module type from the crate name.
	#[clap(alias = "auto")]
	Automatic,
	/// Service module.
	#[clap(alias = "serv")]
	Service,
	/// Authorizer module.
	#[clap(alias = "auth")]
	Authorizer,
	/// CoreVM guest code.
	#[clap(alias = "guest")]
	CoreVmGuest,
}

#[derive(clap::ValueEnum, Clone, Debug)]
#[value(rename_all = "lowercase")]
enum Profile {
	/// The "debug" profile (debug symbols and no optimizations).
	Debug,
	/// The "release" profile (debug symbols and optimizations).
	Release,
	/// The "production" profile (optimizations and no debug symbols).
	Production,
}
impl From<Profile> for ProfileType {
	fn from(profile: Profile) -> ProfileType {
		match profile {
			Profile::Debug => ProfileType::Debug,
			Profile::Release => ProfileType::Release,
			Profile::Production => ProfileType::Other("production"),
		}
	}
}

/// CLI utility for building PVM code blobs, particularly services and authorizers.
#[derive(Parser, Debug)]
#[command(name = "jam-pvm-build", version, long_version = format!("{}\nGP {}", env!("CARGO_PKG_VERSION"), jam_types::GP_VERSION))]
struct Args {
	/// Path of crate to build; defaults to current directory if not supplied.
	path: Option<std::path::PathBuf>,
	/// Output path; defaults to `<crate-name>.<ext>` in the current directory if not supplied.
	///
	/// If a directory is supplied, output will be placed in `<output-path>/<crate-name>.<ext>`.
	#[arg(short, long)]
	output: Option<std::path::PathBuf>,
	/// Module type to build.
	#[arg(short, long, value_enum, default_value_t = ModuleType::Automatic)]
	module: ModuleType,
	/// Install rustc dependencies if missing.
	#[arg(long)]
	auto_install: bool,
	/// The build profile to use.
	#[arg(short, long, value_enum, default_value_t = Profile::Release)]
	profile: Profile,
}

fn main() -> std::process::ExitCode {
	let args = Args::parse();
	let cd = std::env::current_dir().expect("Unable to get current directory");
	let crate_dir = args.path.unwrap_or_else(|| cd.clone());
	let blob_type = match args.module {
		ModuleType::Automatic => {
			let filename = crate_dir.file_name().and_then(|x| x.to_str()).expect("Invalid path?");
			let con_serv = filename.contains("service");
			let con_auth = filename.contains("authorizer");
			let con_corevm = filename.contains("corevm");
			if filename.ends_with("-service") || (con_serv && !con_auth && !con_corevm) {
				BlobType::Service
			} else if filename.ends_with("-authorizer") || (!con_serv && con_auth && !con_corevm) {
				BlobType::Authorizer
			} else if filename.ends_with("-guest") || (!con_serv && !con_auth && con_corevm) {
				BlobType::CoreVmGuest
			} else {
				panic!("Could not determine module type from crate name");
			}
		},
		ModuleType::Service => BlobType::Service,
		ModuleType::Authorizer => BlobType::Authorizer,
		ModuleType::CoreVmGuest => BlobType::CoreVmGuest,
	};

	let out_dir = TempDir::new().expect("Could not create temporary directory");
	let (crate_name, pvm_path, _hash_path) = jam_pvm_builder::build_pvm_blob(
		&crate_dir,
		blob_type,
		out_dir.path(),
		args.auto_install,
		args.profile.into(),
	);

	let output_file = match args.output {
		Some(output_path) if output_path.is_dir() =>
			blob_type.output_file(&output_path, &crate_name),
		Some(output_path) => output_path,
		None => blob_type.output_file(&cd, &crate_name),
	};

	std::fs::copy(pvm_path, &output_file).expect("Unable to write to output file");

	let blob_type_name = match blob_type {
		BlobType::Service | BlobType::Authorizer => "JAM",
		BlobType::CoreVmGuest => "CoreVM",
	};
	println!("Written {blob_type_name}-PVM blob for {crate_name} to {}...", output_file.display());
	std::process::ExitCode::SUCCESS
}