diff --git a/src/main.rs b/src/main.rs index 0b1e0a74..d044531f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,9 @@ use base64::display::Base64Display; use base64::engine::general_purpose::STANDARD; use clap::{Arg, ArgAction, Command}; -use simplicityhl::ast::ElementsJetHinter; +use simplicityhl::ast::{CoreJetHinter, ElementsJetHinter, JetHinter}; +#[cfg(feature = "external-jets")] +use simplicityhl::jet::external::{init_external_jet_lib, ExternalJetHinter}; use simplicityhl::version::SimcDirective; use simplicityhl::{ resolution::DependencyMapBuilder, source::CanonPath, source::CanonSourceFile, AbiMeta, @@ -10,6 +12,7 @@ use simplicityhl::{ }; use simplicityhl::{UnstableFeature, UnstableFeatures}; use std::path::Path; +use std::str::FromStr; use std::{env, fmt}; #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -44,6 +47,82 @@ impl fmt::Display for Output { } } +#[derive(Clone)] +enum Target { + Elements, + Core, + #[cfg(feature = "external-jets")] + External(String), +} + +#[derive(Debug)] +struct TargetParseError(String); + +impl fmt::Display for TargetParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for TargetParseError {} + +#[cfg(feature = "external-jets")] +const TARGET_HELP: &str = + "Target platform for compilation (e.g., --target=elements|core or --target=external:./my_target.dylib)"; + +#[cfg(not(feature = "external-jets"))] +const TARGET_HELP: &str = "Target platform for compilation (e.g., --target=elements|core)"; + +impl Target { + #[allow(clippy::unnecessary_wraps)] + fn jet_hinter(&self) -> Result, Box> { + match self { + Target::Elements => Ok(Box::new(ElementsJetHinter::new())), + Target::Core => Ok(Box::new(CoreJetHinter::new())), + #[cfg(feature = "external-jets")] + Target::External(path) => { + unsafe { + init_external_jet_lib(path)?; + } + + Ok(Box::new(ExternalJetHinter::new())) + } + } + } +} + +impl FromStr for Target { + type Err = TargetParseError; + + fn from_str(s: &str) -> Result { + match s { + "elements" => Ok(Target::Elements), + "core" => Ok(Target::Core), + #[cfg(feature = "external-jets")] + _ if s.starts_with("external:") => { + let path = s.trim_start_matches("external:"); + if path.is_empty() { + Err(TargetParseError( + "Invalid target: external target path cannot be empty".to_string(), + )) + } else { + Ok(Target::External(path.to_string())) + } + } + #[cfg(feature = "external-jets")] + _ => Err(TargetParseError(format!( + "Unknown target: {}. Use one of: elements, core, external:", + s + ))), + #[cfg(not(feature = "external-jets"))] + _ => Err(TargetParseError(format!( + "Unknown target: {}. Use one of: elements, core", + s + ))), + } + } +} + fn main() -> Result<(), Box> { let command = { Command::new(env!("CARGO_BIN_NAME")) @@ -111,6 +190,16 @@ fn main() -> Result<(), Box> { .value_parser(clap::value_parser!(UnstableFeature)) .help(simplicityhl::UnstableFeature::help_message()), ) + .arg( + Arg::new("target") + .long("target") + .short('t') + .value_name("TARGET") + .default_value("elements") + .action(ArgAction::Set) + .value_parser(clap::value_parser!(Target)) + .help(TARGET_HELP), + ) }; let matches = command.get_matches(); @@ -151,6 +240,10 @@ fn main() -> Result<(), Box> { simplicityhl::Arguments::default() }; + let target = matches + .get_one::("target") + .expect("target argument should have a default value"); + let dep_args = matches .get_many::("dependencies") .unwrap_or_default(); @@ -201,7 +294,7 @@ fn main() -> Result<(), Box> { &unstable_features, args_opt, include_debug_symbols, - Box::new(ElementsJetHinter::new()), + target.jet_hinter()?, ) { Ok(program) => program, Err(e) => {