use camino::Utf8PathBuf;
use clap::{Args, Parser, Subcommand, ValueEnum};
use std::fmt;
use uniffi_bindgen::bindings::*;
use uniffi_bindgen::pipeline::initial;
use uniffi_pipeline::PrintOptions;
#[derive(Copy, Clone, ValueEnum)]
enum TargetLanguageArg {
Kotlin,
Swift,
Python,
Ruby,
}
impl fmt::Display for TargetLanguageArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Kotlin => write!(f, "kotlin"),
Self::Swift => write!(f, "swift"),
Self::Python => write!(f, "python"),
Self::Ruby => write!(f, "ruby"),
}
}
}
impl From<TargetLanguageArg> for TargetLanguage {
fn from(l: TargetLanguageArg) -> Self {
match l {
TargetLanguageArg::Kotlin => Self::Kotlin,
TargetLanguageArg::Swift => Self::Swift,
TargetLanguageArg::Python => Self::Python,
TargetLanguageArg::Ruby => Self::Ruby,
}
}
}
#[derive(Parser)]
#[clap(name = "uniffi-bindgen")]
#[clap(version = clap::crate_version!())]
#[clap(propagate_version = true)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Generate {
#[clap(long, short, value_enum)]
language: Vec<TargetLanguageArg>,
#[clap(long, short)]
out_dir: Option<Utf8PathBuf>,
#[clap(long, short)]
no_format: bool,
#[clap(long, short)]
config: Option<Utf8PathBuf>,
#[clap(long = "library")]
_library_mode: bool,
#[clap(long = "crate")]
crate_name: Option<String>,
source: Utf8PathBuf,
#[clap(long)]
metadata_no_deps: bool,
},
Scaffolding {
#[clap(long, short)]
out_dir: Option<Utf8PathBuf>,
#[clap(long, short)]
no_format: bool,
udl_file: Utf8PathBuf,
},
Pipeline(PipelineArgs),
}
#[derive(Args)]
struct PipelineArgs {
#[clap(long = "library")]
library_mode: bool,
source: Utf8PathBuf,
#[clap(long = "crate")]
crate_name: Option<String>,
#[clap(long)]
metadata_no_deps: bool,
language: TargetLanguageArg,
#[clap(short, long)]
pass: Option<String>,
#[clap(long)]
no_diff: bool,
#[clap(short = 't', long = "type")]
filter_type: Option<String>,
#[clap(short = 'n', long = "name")]
filter_name: Option<String>,
}
pub fn run_main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Generate {
language,
out_dir,
no_format,
config,
source,
crate_name,
metadata_no_deps,
..
} => {
if language.is_empty() {
panic!("please specify at least one language with --language")
}
generate(GenerateOptions {
languages: language.into_iter().map(TargetLanguage::from).collect(),
out_dir: out_dir
.expect("--out-dir is required when generating {language} bindings"),
source,
config_override: config,
crate_filter: crate_name,
metadata_no_deps,
format: !no_format,
})?;
}
Commands::Scaffolding {
out_dir,
no_format,
udl_file,
} => {
uniffi_bindgen::generate_component_scaffolding(
&udl_file,
out_dir.as_deref(),
!no_format,
)?;
}
Commands::Pipeline(args) => {
let mut paths = uniffi_bindgen::BindgenPaths::default();
#[cfg(feature = "cargo-metadata")]
paths.add_cargo_metadata_layer(args.metadata_no_deps)?;
let initial_root = if args.library_mode {
initial::Root::from_library(paths, &args.source, args.crate_name)?
} else {
initial::Root::from_udl(paths, &args.source, args.crate_name)?
};
let opts = PrintOptions {
pass: args.pass,
no_diff: args.no_diff,
filter_type: args.filter_type,
filter_name: args.filter_name,
};
match args.language {
TargetLanguageArg::Python => python::pipeline().print_passes(initial_root, opts)?,
language => unimplemented!("{language} does not use the bindings IR pipeline yet"),
};
}
};
Ok(())
}