From c66dfdd52a843f0b312b4b80a1edb82d336916bd Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Sun, 27 Mar 2022 23:46:25 -0700 Subject: [PATCH] Use structopt to capture generate options --- Cargo.toml | 8 +- src/bin/generate-bindings.rs | 145 +++++++++++++++++++++++++---------- 2 files changed, 108 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2df73f8..7fedfd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,14 +20,10 @@ uniffi = { version = "0.16.0", features = ["builtin-bindgen"] } thiserror = "1.0" anyhow = "=1.0.45" # remove after upgrading to next version of uniffi uniffi_bindgen = "0.16.0" - -#uniffi_bindgen = { version = "0.16.0", optional = true } +structopt = "0.3" [build-dependencies] uniffi_build = { version = "0.16.0", features = ["builtin-bindgen"] } -[features] -#generate-python = ["uniffi_bindgen"] - [[bin]] -name = "generate" +name = "generate-bindings" diff --git a/src/bin/generate-bindings.rs b/src/bin/generate-bindings.rs index 3e77a9a..af6fa08 100644 --- a/src/bin/generate-bindings.rs +++ b/src/bin/generate-bindings.rs @@ -1,67 +1,134 @@ +use std::fmt; +use std::str::FromStr; +use structopt::StructOpt; use uniffi_bindgen; pub const BDK_UDL: &str = "src/bdk.udl"; -#[derive(Debug)] -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub enum Language { KOTLIN, - // PYTHON, - // SWIFT, - UNSUPPORTED, + PYTHON, + SWIFT, } -impl Language { - fn to_string(self) -> &'static str { - if self == Language::KOTLIN { "kotlin" } - // else if self == Language::PYTHON { "python" } - // else if self == Language::SWIFT { "swift" } - else { panic!("Not a supported language") } +impl fmt::Display for Language { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Language::KOTLIN => write!(f, "kotlin"), + Language::SWIFT => write!(f, "swift"), + Language::PYTHON => write!(f, "python"), + } } } -fn parse_language(language_argument: &str) -> Language { - match language_argument { - "kotlin" => Language::KOTLIN, - // "python" => Language::PYTHON, - // "swift" => Language::SWIFT, - _ => panic!("Unsupported language") - } +#[derive(Debug)] +pub enum Error { + UnsupportedLanguage, } -fn generate_bindings() -> Result<(), Box> { - use std::env; - let args: Vec = env::args().collect(); - let language: Language; - let output_directory: &String; - - if &args[1] != "--language" { - panic!("Please provide the --language option"); - } else { - language = parse_language(&args[2]); +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) } +} - if &args[3] != "--out-dir" { - panic!("Please provide the --out-dir option"); - } else { - output_directory = &args[4] +impl FromStr for Language { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "kotlin" => Ok(Language::KOTLIN), + "python" => Ok(Language::PYTHON), + "swift" => Ok(Language::SWIFT), + _ => Err(Error::UnsupportedLanguage), + } } +} - println!("Chosen language is {:?}", language); - println!("Output directory is {:?}", output_directory); - +fn generate_bindings(opt: &Opt) -> anyhow::Result<(), anyhow::Error> { uniffi_bindgen::generate_bindings( &format!("{}/{}", env!("CARGO_MANIFEST_DIR"), BDK_UDL), None, - vec![language.to_string()], - Some(&output_directory), + vec![opt.language.to_string().as_str()], + Some(&opt.out_dir), false, )?; Ok(()) } -fn main() -> Result<(), Box> { - generate_bindings()?; +fn fixup_python_lib_path>( + out_dir: O, + lib_name: &str, +) -> Result<(), Box> { + use std::fs; + use std::io::Write; + + const LOAD_INDIRECT_DEF: &str = "def loadIndirect():"; + + let bindings_file = out_dir.as_ref().join("bdk.py"); + let mut data = fs::read_to_string(&bindings_file)?; + + let pos = data.find(LOAD_INDIRECT_DEF).expect(&format!( + "loadIndirect not found in `{}`", + bindings_file.display() + )); + let range = pos..pos + LOAD_INDIRECT_DEF.len(); + + let replacement = format!( + r#" +def loadIndirect(): + import glob + return getattr(ctypes.cdll, glob.glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), '{}.*'))[0]) + +def _loadIndirectOld():"#, + lib_name + ); + data.replace_range(range, &replacement); + + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(&bindings_file)?; + file.write(data.as_bytes())?; + + Ok(()) +} + +#[derive(Debug, StructOpt)] +#[structopt( + name = "generate-bindings", + about = "A tool to generate bdk-ffi language bindings" +)] +struct Opt { + /// Language to generate bindings for + #[structopt(env = "UNIFFI_BINDGEN_LANGUAGE", short, long, possible_values(&["kotlin","swift","python"]), parse(try_from_str = Language::from_str))] + language: Language, + + /// Output directory to put generated language bindings + #[structopt(env = "UNIFFI_BINDGEN_OUTPUT_DIR", short, long)] + out_dir: String, + + /// Python fix up lib path + #[structopt(env = "UNIFFI_BINDGEN_PYTHON_FIXUP_PATH", short, long)] + python_fixup_path: Option, +} + +fn main() -> Result<(), Box> { + let opt = Opt::from_args(); + println!("{:?}", opt); + + println!("Chosen language is {}", opt.language); + println!("Output directory is {}", opt.out_dir); + println!("Python fix up lib path is {:?}", opt.python_fixup_path); + + generate_bindings(&opt)?; + + if opt.language == Language::PYTHON { + if let Some(name) = opt.python_fixup_path { + println!("Fixing up python lib path"); + fixup_python_lib_path(&opt.out_dir, &name)?; + } + } Ok(()) }