diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bc3665f..eb9abd16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,11 +141,8 @@ jobs: run: Copy-Item -Path .cargo/config-windows.toml -Destination .cargo/config.toml - name: Caching uses: Swatinem/rust-cache@v1 - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release + - name: Build FFI and JNI + run: cargo build -p foo-ffi -p foo-ffi-java --release --target ${{ matrix.target }} - name: C bindings uses: actions-rs/cargo@v1 with: @@ -190,11 +187,8 @@ jobs: override: true - name: Caching uses: Swatinem/rust-cache@v1 - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release + - name: Build FFI and JNI + run: cargo build -p foo-ffi -p foo-ffi-java --release - name: C bindings uses: actions-rs/cargo@v1 with: @@ -247,12 +241,10 @@ jobs: toolchain: stable target: ${{ matrix.target }} override: true - - name: Build - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: build - args: --release --target ${{ matrix.target }} + - name: Install Rust Cross + run: cargo install cross + - name: Build FFI and JNI + run: cross build -p foo-ffi -p foo-ffi-java --release --target ${{ matrix.target }} - name: C bindings uses: actions-rs/cargo@v1 with: diff --git a/Cargo.lock b/Cargo.lock index 96bbae2d..7ea54300 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,24 +147,37 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.17" +version = "4.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "6ea54a38e4bce14ff6931c72e5b3c43da7051df056913d4e7e1fcdb1c03df69d" dependencies = [ "atty", "bitflags", + "clap_derive", "clap_lex", - "indexmap", + "once_cell", "strsim", "termcolor", - "textwrap", +] + +[[package]] +name = "clap_derive" +version = "4.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -189,8 +202,10 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" name = "dotnet-oo-bindgen" version = "0.3.0" dependencies = [ + "clap", "dunce", "oo-bindgen", + "tracing", ] [[package]] @@ -240,12 +255,6 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "heck" version = "0.3.3" @@ -255,6 +264,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -277,16 +292,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "itoa" version = "1.0.3" @@ -415,7 +420,7 @@ name = "oo-bindgen" version = "0.3.0" dependencies = [ "backtrace", - "heck", + "heck 0.3.3", "lazy_static", "platforms", "regex", @@ -458,6 +463,30 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.43" @@ -506,7 +535,7 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" name = "rust-oo-bindgen" version = "0.3.0" dependencies = [ - "heck", + "heck 0.3.3", "oo-bindgen", ] @@ -607,12 +636,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thiserror" version = "1.0.32" @@ -742,6 +765,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/ci-script/Cargo.toml b/ci-script/Cargo.toml index c21c40bb..bc20d620 100644 --- a/ci-script/Cargo.toml +++ b/ci-script/Cargo.toml @@ -9,6 +9,6 @@ oo-bindgen = { path = "../oo-bindgen" } c-oo-bindgen = { path = "../generators/c-oo-bindgen" } dotnet-oo-bindgen = { path = "../generators/dotnet-oo-bindgen" } java-oo-bindgen = { path = "../generators/java-oo-bindgen" } -clap = "3.2.17" +clap = { version = "^4", features = ["derive"] } pathdiff = "0.2" tracing = "^0.1" \ No newline at end of file diff --git a/ci-script/src/builders/dotnet.rs b/ci-script/src/builders/dotnet.rs index a478a7c5..ca304d18 100644 --- a/ci-script/src/builders/dotnet.rs +++ b/ci-script/src/builders/dotnet.rs @@ -1,10 +1,12 @@ use crate::{BindingBuilder, BindingBuilderSettings}; +use dotnet_oo_bindgen::TargetFramework; use oo_bindgen::backend::PlatformLocations; use std::path::PathBuf; use std::process::Command; pub(crate) struct DotnetBindingBuilder { settings: BindingBuilderSettings, + target_framework: TargetFramework, platforms: PlatformLocations, extra_files: Vec, } @@ -12,11 +14,13 @@ pub(crate) struct DotnetBindingBuilder { impl DotnetBindingBuilder { pub(crate) fn new( settings: BindingBuilderSettings, + target_framework: TargetFramework, platforms: PlatformLocations, extra_files: &[PathBuf], ) -> Self { Self { settings, + target_framework, platforms, extra_files: extra_files.to_vec(), } @@ -52,6 +56,7 @@ impl BindingBuilder for DotnetBindingBuilder { extra_files: self.extra_files.clone(), platforms: self.platforms.clone(), generate_doxygen, + target_framework: self.target_framework, }; dotnet_oo_bindgen::generate_dotnet_bindings(&self.settings.library, &config).unwrap(); diff --git a/ci-script/src/cli.rs b/ci-script/src/cli.rs index 2c1dd48b..aa44b5c4 100644 --- a/ci-script/src/cli.rs +++ b/ci-script/src/cli.rs @@ -1,49 +1,32 @@ -use clap::{App, Arg, ArgMatches}; +use clap::Parser; +use dotnet_oo_bindgen::TargetFramework; +use std::path::PathBuf; -pub(crate) fn build() -> ArgMatches { - App::new("oo-bindgen") - .arg( - Arg::with_name("c") - .long("c") - .takes_value(false) - .help("Build C bindings"), - ) - .arg( - Arg::with_name("dotnet") - .long("dotnet") - .takes_value(false) - .help("Build .NET Core bindings"), - ) - .arg( - Arg::with_name("java") - .long("java") - .takes_value(false) - .help("Build Java (JNI) bindings"), - ) - .arg( - Arg::with_name("doxygen") - .long("doxygen") - .takes_value(false) - .help("Generate Doxygen documentation"), - ) - .arg( - Arg::with_name("no-tests") - .long("no-tests") - .takes_value(false) - .help("Do not run the unit tests"), - ) - .arg( - Arg::with_name("package") - .long("package") - .takes_value(true) - .help("Generate package with the provided modules"), - ) - .arg( - Arg::with_name("extra-files") - .short('f') - .long("extra-files") - .takes_value(true) - .help("Path to extra files to include in the generated bindings"), - ) - .get_matches() +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +pub(crate) struct Args { + /// build the C bindings + #[arg(long = "c", default_value_t = false)] + pub(crate) build_c: bool, + /// build the .NET bindings + #[arg(long = "dotnet", default_value_t = false)] + pub(crate) build_dotnet: bool, + /// build the Java bindings + #[arg(long = "java", default_value_t = false)] + pub(crate) build_java: bool, + /// Target .NET framework, which indirectly determines the C# language version + #[arg(value_enum, short = 't', long = "target-dotnet-framework", default_value_t = TargetFramework::NetStandard2_0)] + pub(crate) target_framework: TargetFramework, + /// generate doxygen documentation + #[arg(long = "doxygen", default_value_t = false)] + pub(crate) generate_doxygen: bool, + /// do NOT run the unit tests + #[arg(long = "no-tests", default_value_t = false)] + pub(crate) no_tests: bool, + /// Generate package from the provided directory + #[arg(long = "package")] + pub(crate) package: Option, + /// Path(s) to extra files to include in the generated bindings + #[arg(short = 'f', long = "extra-files")] + pub(crate) extra_files: Vec, } diff --git a/ci-script/src/lib.rs b/ci-script/src/lib.rs index b3ce6951..3e212ae4 100644 --- a/ci-script/src/lib.rs +++ b/ci-script/src/lib.rs @@ -1,6 +1,9 @@ pub(crate) mod builders; pub(crate) mod cli; +// re-export this so that dependencies don't need the dotnet generator directly +pub use dotnet_oo_bindgen::TargetFramework; + use std::fs; use std::path::PathBuf; use std::rc::Rc; @@ -8,6 +11,8 @@ use std::rc::Rc; use oo_bindgen::backend::*; use oo_bindgen::model::Library; +use clap::Parser; + const SUPPORTED_PLATFORMS: &[&Platform] = &[ &platform::X86_64_PC_WINDOWS_MSVC, &platform::I686_PC_WINDOWS_MSVC, @@ -25,24 +30,17 @@ fn is_officially_supported(p: &Platform) -> bool { } pub fn run(settings: BindingBuilderSettings) { - let matches = cli::build(); + let args = crate::cli::Args::parse(); - let mut run_tests = !matches.is_present("no-tests"); + let mut run_tests = !args.no_tests; - let run_c = matches.is_present("c"); - let run_dotnet = matches.is_present("dotnet"); - let run_java = matches.is_present("java"); - let run_all = !run_c && !run_dotnet && !run_java; + // if no languages are selected, we build all of them + let run_all = !args.build_c && !args.build_dotnet && !args.build_java; - let package = matches.is_present("package"); - let package_src = matches.value_of("package"); - - let extra_files = matches - .values_of("extra-files") - .map_or(Vec::new(), |v| v.map(PathBuf::from).collect()); + let package = args.package.is_some(); let mut platforms = PlatformLocations::new(); - if let Some(package_src) = package_src { + if let Some(package_src) = args.package { for entry in fs::read_dir(package_src).unwrap() { let entry = entry.unwrap(); let path = entry.path(); @@ -71,28 +69,27 @@ pub fn run(settings: BindingBuilderSettings) { assert!(!platforms.is_empty(), "No platforms found!"); - let doxygen = matches.is_present("doxygen"); - - if run_c || run_all { + if args.build_c || run_all { let mut builder = crate::builders::c::CBindingBuilder::new( settings.clone(), platforms.clone(), - &extra_files, + &args.extra_files, ); - builder.run(run_tests, package, doxygen); + builder.run(run_tests, package, args.generate_doxygen); } - if run_dotnet || run_all { + if args.build_dotnet || run_all { let mut builder = crate::builders::dotnet::DotnetBindingBuilder::new( settings.clone(), + args.target_framework, platforms.clone(), - &extra_files, + &args.extra_files, ); - builder.run(run_tests, package, doxygen); + builder.run(run_tests, package, args.generate_doxygen); } - if run_java || run_all { + if args.build_java || run_all { let mut builder = - crate::builders::java::JavaBindingBuilder::new(settings, platforms, &extra_files); - builder.run(run_tests, package, doxygen); + crate::builders::java::JavaBindingBuilder::new(settings, platforms, &args.extra_files); + builder.run(run_tests, package, args.generate_doxygen); } } diff --git a/generators/c-oo-bindgen/src/cpp/header.rs b/generators/c-oo-bindgen/src/cpp/header.rs index b64cf2d4..c05538bb 100644 --- a/generators/c-oo-bindgen/src/cpp/header.rs +++ b/generators/c-oo-bindgen/src/cpp/header.rs @@ -450,13 +450,79 @@ where f.newline() } +trait ToConstantCpp { + fn to_constant_cpp(&self) -> String; +} + +impl ToConstantCpp for PrimitiveValue { + fn to_constant_cpp(&self) -> String { + match self { + PrimitiveValue::Bool(x) => x.to_string(), + PrimitiveValue::U8(x) => x.to_string(), + PrimitiveValue::S8(x) => x.to_string(), + PrimitiveValue::U16(x) => x.to_string(), + PrimitiveValue::S16(x) => x.to_string(), + PrimitiveValue::U32(x) => x.to_string(), + PrimitiveValue::S32(x) => x.to_string(), + PrimitiveValue::U64(x) => x.to_string(), + PrimitiveValue::S64(x) => x.to_string(), + PrimitiveValue::Float(x) => x.to_string(), + PrimitiveValue::Double(x) => x.to_string(), + } + } +} + +impl ToConstantCpp for DurationValue { + fn to_constant_cpp(&self) -> String { + match self { + DurationValue::Milliseconds(x) => format!("std::chrono::milliseconds({})", x), + DurationValue::Seconds(x) => format!("std::chrono::Duration::seconds({})", x), + } + } +} + +impl ToConstantCpp for EnumValue { + fn to_constant_cpp(&self) -> String { + format!("{}::{}", self.handle.core_cpp_type(), self.variant.name) + } +} + +impl ToConstantCpp for BasicValue { + fn to_constant_cpp(&self) -> String { + match self { + BasicValue::Primitive(x) => x.to_constant_cpp(), + BasicValue::Duration(x) => x.to_constant_cpp(), + BasicValue::Enum(x) => x.to_constant_cpp(), + } + } +} + +impl ToConstantCpp for DefaultCallbackReturnValue { + fn to_constant_cpp(&self) -> String { + match self { + DefaultCallbackReturnValue::Basic(x) => x.to_constant_cpp(), + DefaultCallbackReturnValue::InitializedStruct(x) => { + match x.initializer.initializer_type { + // normal default constructor + InitializerType::Normal => format!("{}()", x.handle.core_cpp_type()), + // static factory method + InitializerType::Static => { + format!("{}::{}()", x.handle.core_cpp_type(), x.initializer.name) + } + } + } + } + } +} + fn print_interface( f: &mut dyn Printer, handle: &Handle>, ) -> FormattingResult<()> { doxygen(f, |f| { print_cpp_doc(f, &handle.doc)?; - f.writeln("@note this class is an \"interface\" and only has pure virtual methods") + f.newline()?; + f.writeln("@note this class is an \"interface\" and only has virtual methods, some of which may have default implementations.") })?; f.writeln(&format!("class {} {{", handle.core_cpp_type()))?; f.writeln("public:")?; @@ -477,6 +543,11 @@ fn print_interface( .collect::>() .join(", "); + let default_value = cb + .default_implementation + .as_ref() + .map(|v| v.to_constant_cpp()); + doxygen(f, |f| { print_cpp_doc(f, &cb.doc)?; f.newline()?; @@ -485,14 +556,37 @@ fn print_interface( print_cpp_argument_doc(f, arg)?; } print_cpp_return_type_doc(f, &cb.return_type)?; + if let Some(x) = &default_value { + f.newline()?; + f.writeln(&format!( + "@note This method has a default implementation that returns '{}'", + x + ))?; + } Ok(()) })?; - f.writeln(&format!( - "virtual {} {}({}) = 0;", - cb.return_type.get_cpp_callback_return_type(), - cb.core_cpp_type(), - args - ))?; + + match &default_value { + None => { + f.writeln(&format!( + "virtual {} {}({}) = 0;", + cb.return_type.get_cpp_callback_return_type(), + cb.core_cpp_type(), + args + ))?; + } + Some(v) => { + f.writeln(&format!( + "virtual {} {}({}) {{", + cb.return_type.get_cpp_callback_return_type(), + cb.core_cpp_type(), + args + ))?; + indented(f, |f| f.writeln(&format!("return {};", v)))?; + f.writeln("}")?; + } + } + f.newline()?; } Ok(()) })?; diff --git a/generators/dotnet-oo-bindgen/Cargo.toml b/generators/dotnet-oo-bindgen/Cargo.toml index f7f18910..c93c6704 100644 --- a/generators/dotnet-oo-bindgen/Cargo.toml +++ b/generators/dotnet-oo-bindgen/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" [dependencies] oo-bindgen = { path = "../../oo-bindgen" } dunce = "1.0" +tracing = "^0.1" +clap = { version = "^4", features = ["derive"] } diff --git a/generators/dotnet-oo-bindgen/src/interface.rs b/generators/dotnet-oo-bindgen/src/interface.rs index 7df9be0b..4440d86b 100644 --- a/generators/dotnet-oo-bindgen/src/interface.rs +++ b/generators/dotnet-oo-bindgen/src/interface.rs @@ -5,12 +5,86 @@ use crate::conversion::{base_functor_type, full_functor_type, TypeInfo}; use crate::doc::{docstring_print, xmldoc_print}; use crate::formatting::{documentation, namespaced}; use crate::helpers::call_dotnet_function; -use crate::{print_imports, print_license}; +use crate::{print_imports, print_license, TargetFramework}; + +trait ConstantReturnValue { + fn get_constant_return_value(&self) -> String; +} + +impl ConstantReturnValue for PrimitiveValue { + fn get_constant_return_value(&self) -> String { + match self { + PrimitiveValue::Bool(x) => x.to_string(), + PrimitiveValue::U8(x) => x.to_string(), + PrimitiveValue::S8(x) => x.to_string(), + PrimitiveValue::U16(x) => x.to_string(), + PrimitiveValue::S16(x) => x.to_string(), + PrimitiveValue::U32(x) => x.to_string(), + PrimitiveValue::S32(x) => x.to_string(), + PrimitiveValue::U64(x) => x.to_string(), + PrimitiveValue::S64(x) => x.to_string(), + PrimitiveValue::Float(x) => x.to_string(), + PrimitiveValue::Double(x) => x.to_string(), + } + } +} + +impl ConstantReturnValue for EnumValue { + fn get_constant_return_value(&self) -> String { + format!( + "{}.{}", + self.handle.name.camel_case(), + self.variant.name.camel_case() + ) + } +} + +impl ConstantReturnValue for DurationValue { + fn get_constant_return_value(&self) -> String { + match self { + DurationValue::Milliseconds(x) => format!("TimeSpan.FromMilliseconds({})", x), + DurationValue::Seconds(x) => format!("TimeSpan.FromSeconds({})", x), + } + } +} + +impl ConstantReturnValue for BasicValue { + fn get_constant_return_value(&self) -> String { + match self { + BasicValue::Primitive(x) => x.get_constant_return_value(), + BasicValue::Duration(x) => x.get_constant_return_value(), + BasicValue::Enum(x) => x.get_constant_return_value(), + } + } +} + +impl ConstantReturnValue for ZeroParameterStructInitializer { + fn get_constant_return_value(&self) -> String { + match self.initializer.initializer_type { + InitializerType::Normal => format!("new {}()", self.handle.name().camel_case()), + InitializerType::Static => format!( + "{}.{}()", + self.handle.name().camel_case(), + self.initializer.name.camel_case() + ), + } + } +} + +impl ConstantReturnValue for DefaultCallbackReturnValue { + fn get_constant_return_value(&self) -> String { + match self { + DefaultCallbackReturnValue::Basic(x) => x.get_constant_return_value(), + DefaultCallbackReturnValue::InitializedStruct(x) => x.get_constant_return_value(), + } + } +} pub(crate) fn generate( f: &mut dyn Printer, interface: &InterfaceType, lib: &Library, + framework: TargetFramework, ) -> FormattingResult<()> { let interface_name = format!("I{}", interface.name().camel_case()); @@ -81,7 +155,23 @@ pub(crate) fn generate( .collect::>() .join(", "), )?; - f.write(");") + match &func.default_implementation { + None => { + f.write(");") + } + Some(di) => { + if framework.supports_default_interface_methods() { + f.write(") {")?; + indented(f, |f| { + f.writeln(&format!("return {};", di.get_constant_return_value())) + })?; + f.writeln("}") + } else { + tracing::warn!("Method {}::{} has a default implementation defined, but it cannot be supported in C# 7.3", interface.name().camel_case(), func.name.camel_case()); + f.write(");") + } + } + } }) })?; diff --git a/generators/dotnet-oo-bindgen/src/lib.rs b/generators/dotnet-oo-bindgen/src/lib.rs index 8e9d1b21..824933db 100644 --- a/generators/dotnet-oo-bindgen/src/lib.rs +++ b/generators/dotnet-oo-bindgen/src/lib.rs @@ -44,6 +44,7 @@ clippy::all bare_trait_objects )] +use std::fmt::Formatter; use std::fs; use std::io::Write; use std::path::Path; @@ -75,12 +76,51 @@ const SUPPORTED_PLATFORMS: &[Platform] = &[ platform::AARCH64_UNKNOWN_LINUX_GNU, ]; +/// Target framework - affects runtime compatible and allowed language features +/// +/// Default C# versions for different targets specified here: +/// +/// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version +/// +#[derive(Debug, Copy, Clone, clap::ValueEnum)] +pub enum TargetFramework { + /// .NET Standard 2.0 - Compatible with .NET Framework 4.6.1 -> 4.8 + /// Defaults to C# 7.3 + NetStandard2_0, + /// .NET Standard 2.1 - NOT compatible with any .NET Framework + /// Defaults to C# 8.0 + NetStandard2_1, +} + +impl std::fmt::Display for TargetFramework { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl TargetFramework { + pub(crate) fn get_target_framework_str(&self) -> &'static str { + match self { + TargetFramework::NetStandard2_0 => "netstandard2.0", + TargetFramework::NetStandard2_1 => "netstandard2.1", + } + } + + pub(crate) fn supports_default_interface_methods(&self) -> bool { + match self { + TargetFramework::NetStandard2_0 => false, + TargetFramework::NetStandard2_1 => true, + } + } +} + pub struct DotnetBindgenConfig { pub output_dir: PathBuf, pub ffi_name: &'static str, pub extra_files: Vec, pub platforms: PlatformLocations, pub generate_doxygen: bool, + pub target_framework: TargetFramework, } pub fn generate_dotnet_bindings( @@ -139,7 +179,10 @@ fn generate_csproj(lib: &Library, config: &DotnetBindgenConfig) -> FormattingRes f.writeln("")?; f.writeln(" ")?; - f.writeln(" netstandard2.0")?; + f.writeln(&format!( + " {}", + config.target_framework.get_target_framework_str() + ))?; f.writeln(" true")?; f.writeln(" true")?; // Include symbols f.writeln(" snupkg")?; // Use new file format @@ -469,7 +512,7 @@ fn generate_interfaces(lib: &Library, config: &DotnetBindgenConfig) -> Formattin filename.set_extension("cs"); let mut f = FilePrinter::new(filename)?; - interface::generate(&mut f, interface, lib)?; + interface::generate(&mut f, interface, lib, config.target_framework)?; } Ok(()) diff --git a/generators/java-oo-bindgen/src/java/interface.rs b/generators/java-oo-bindgen/src/java/interface.rs index 0270d31d..e959b1ee 100644 --- a/generators/java-oo-bindgen/src/java/interface.rs +++ b/generators/java-oo-bindgen/src/java/interface.rs @@ -1,6 +1,71 @@ use super::doc::*; use super::*; +trait ToConstantValue { + fn get_constant_value(&self) -> String; +} + +impl ToConstantValue for PrimitiveValue { + fn get_constant_value(&self) -> String { + match self { + PrimitiveValue::Bool(x) => x.to_string(), + PrimitiveValue::U8(x) => format!("UByte.valueOf({})", x), + PrimitiveValue::S8(x) => x.to_string(), + PrimitiveValue::U16(x) => format!("UShort.valueOf({})", x), + PrimitiveValue::S16(x) => x.to_string(), + PrimitiveValue::U32(x) => format!("UInteger.valueOf({})", x), + PrimitiveValue::S32(x) => x.to_string(), + PrimitiveValue::U64(x) => format!("ULong.valueOf({})", x), + PrimitiveValue::S64(x) => x.to_string(), + PrimitiveValue::Float(x) => x.to_string(), + PrimitiveValue::Double(x) => x.to_string(), + } + } +} + +impl ToConstantValue for DurationValue { + fn get_constant_value(&self) -> String { + match self { + DurationValue::Milliseconds(x) => format!("java.time.Duration.ofMillis({})", x), + DurationValue::Seconds(x) => format!("java.time.Duration.ofSeconds({})", x), + } + } +} + +impl ToConstantValue for BasicValue { + fn get_constant_value(&self) -> String { + match self { + BasicValue::Primitive(x) => x.get_constant_value(), + BasicValue::Duration(x) => x.get_constant_value(), + BasicValue::Enum(x) => { + format!( + "{}.{}", + x.handle.name.camel_case(), + x.variant.name.capital_snake_case() + ) + } + } + } +} + +impl ToConstantValue for DefaultCallbackReturnValue { + fn get_constant_value(&self) -> String { + match self { + DefaultCallbackReturnValue::Basic(x) => x.get_constant_value(), + DefaultCallbackReturnValue::InitializedStruct(x) => { + match x.initializer.initializer_type { + InitializerType::Normal => format!("new {}()", x.handle.name().camel_case()), + InitializerType::Static => format!( + "{}.{}()", + x.handle.name().camel_case(), + x.initializer.name.mixed_case() + ), + } + } + } + } +} + pub(crate) fn generate( f: &mut dyn Printer, interface: &Handle>, @@ -19,12 +84,25 @@ pub(crate) fn generate( blocked(f, |f| { // Write each required method for func in interface.callbacks.iter() { + let constant_return_value = func + .default_implementation + .as_ref() + .map(|x| x.get_constant_value()); + // Documentation documentation(f, |f| { // Print top-level documentation javadoc_print(f, &func.doc)?; f.newline()?; + if let Some(v) = &constant_return_value { + f.writeln(&format!( + "

The default implementation of this method returns '{}'

", + v + ))?; + f.newline()?; + } + // Print each argument value for arg in &func.arguments { f.writeln(&format!("@param {} ", arg.name.mixed_case()))?; @@ -40,9 +118,16 @@ pub(crate) fn generate( Ok(()) })?; + let modifier = if func.default_implementation.is_some() { + "default " + } else { + "" + }; + // Callback signature f.writeln(&format!( - "{} {}(", + "{}{} {}(", + modifier, func.return_type.as_java_primitive(), func.name.mixed_case() ))?; @@ -60,7 +145,15 @@ pub(crate) fn generate( .collect::>() .join(", "), )?; - f.write(");")?; + + match constant_return_value { + None => f.write(");")?, + Some(v) => { + f.write(")")?; + blocked(f, |f| f.writeln(&format!("return {};", v)))?; + } + } + f.newline()?; } Ok(()) diff --git a/generators/java-oo-bindgen/src/rust/interface.rs b/generators/java-oo-bindgen/src/rust/interface.rs index 5b0d720e..47a5c5d7 100644 --- a/generators/java-oo-bindgen/src/rust/interface.rs +++ b/generators/java-oo-bindgen/src/rust/interface.rs @@ -145,13 +145,13 @@ pub(crate) fn generate_interfaces_cache( // Convert return value if let Some(return_type) = &cb.return_type.get_value() { - if let Some(converted) = - return_type.to_rust(&format!("_result.{}", return_type.unwrap_value())) - { + let unwrapped = format!("_result.{}", return_type.unwrap_value()); + + if let Some(converted) = return_type.to_rust(&unwrapped) { let ret = return_type.call_site(&converted).unwrap_or(converted); f.writeln(&format!("return {};", ret))?; } else { - f.writeln("return _result;")?; + f.writeln(&format!("return {};", unwrapped))?; } } diff --git a/oo-bindgen/src/model/builder/interface.rs b/oo-bindgen/src/model/builder/interface.rs index a9b69205..4bbaad4c 100644 --- a/oo-bindgen/src/model/builder/interface.rs +++ b/oo-bindgen/src/model/builder/interface.rs @@ -102,6 +102,7 @@ pub struct CallbackFunctionBuilder<'a> { name: Name, functional_transform: FunctionalTransform, return_type: OptionalReturnType, + default_implementation: Option, arguments: Vec>, doc: Doc, } @@ -113,6 +114,7 @@ impl<'a> CallbackFunctionBuilder<'a> { name, functional_transform: FunctionalTransform::No, return_type: OptionalReturnType::new(), + default_implementation: None, arguments: Vec::new(), doc, } @@ -158,11 +160,30 @@ impl<'a> CallbackFunctionBuilder<'a> { Ok(self) } + pub fn returns_with_default< + T: Into, + D: Into>, + >( + self, + t: T, + d: D, + ) -> BindResult { + let default: DefaultCallbackReturnValue = t.into(); + + // first, try to set the return type to ensure it hasn't previously been set + let mut ret = self.returns(default.get_callback_return_value(), d)?; + + ret.default_implementation = Some(default); + + Ok(ret) + } + pub fn end_callback(mut self) -> BindResult> { let cb = CallbackFunction { name: self.name, functional_transform: self.functional_transform, return_type: self.return_type, + default_implementation: self.default_implementation, arguments: self.arguments, doc: self.doc, }; diff --git a/oo-bindgen/src/model/enum_type.rs b/oo-bindgen/src/model/enum_type.rs index 584288df..8cc28dab 100644 --- a/oo-bindgen/src/model/enum_type.rs +++ b/oo-bindgen/src/model/enum_type.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use crate::model::*; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EnumVariant where T: DocReference, @@ -24,6 +24,12 @@ impl EnumVariant { pub type EnumHandle = Handle>; +impl Handle> { + pub fn value(&self, name: &'static str) -> BindResult { + EnumValue::new(self.clone(), name) + } +} + #[derive(Debug)] pub struct Enum where @@ -62,14 +68,16 @@ where .find(|variant| variant.name.as_ref() == variant_name.as_ref()) } - pub(crate) fn validate_contains_variant_name(&self, variant_name: &str) -> BindResult<()> { - if self.find_variant_by_name(variant_name).is_none() { - Err(BindingError::UnknownEnumVariant { + pub(crate) fn validate_contains_variant_name( + &self, + variant_name: &str, + ) -> BindResult<&EnumVariant> { + match self.find_variant_by_name(variant_name) { + None => Err(BindingError::UnknownEnumVariant { name: self.name.clone(), variant_name: variant_name.to_string(), - }) - } else { - Ok(()) + }), + Some(x) => Ok(x), } } } diff --git a/oo-bindgen/src/model/errors.rs b/oo-bindgen/src/model/errors.rs index 00bb993c..3c5a7798 100644 --- a/oo-bindgen/src/model/errors.rs +++ b/oo-bindgen/src/model/errors.rs @@ -111,6 +111,24 @@ pub enum BindingError { name )] CallbackMethodArgumentWithReservedName { name: Name }, + #[error( + "Initializer '{}' does not exist within struct '{}'", + name, + struct_name + )] + InitializerDoesNotExist { + name: &'static str, + struct_name: Name, + }, + #[error( + "Initializer '{}' within struct '{}' is not parameterless", + name, + struct_name + )] + InitializerNotParameterless { + name: &'static str, + struct_name: Name, + }, // ----------------- struct errors ------------------- #[error("Native struct '{}' was already defined", handle.name)] diff --git a/oo-bindgen/src/model/interface.rs b/oo-bindgen/src/model/interface.rs index c259d374..235416f6 100644 --- a/oo-bindgen/src/model/interface.rs +++ b/oo-bindgen/src/model/interface.rs @@ -66,6 +66,90 @@ impl From for CallbackArgument { } } +/// An enum handle and a default validated variant +#[derive(Debug, Clone)] +pub struct EnumValue { + pub handle: EnumHandle, + pub variant: EnumVariant, +} + +impl EnumValue { + pub(crate) fn new(handle: EnumHandle, variant: &'static str) -> BindResult { + let variant = handle.validate_contains_variant_name(variant)?.clone(); + Ok(Self { handle, variant }) + } +} + +/// Struct handle combined with the validated name of one of it's initializers. +/// The initializer may not take parameters +#[derive(Debug, Clone)] +pub struct ZeroParameterStructInitializer { + pub handle: UniversalStructHandle, + pub initializer: Handle>, +} + +impl ZeroParameterStructInitializer { + fn try_create(handle: UniversalStructHandle, name: &'static str) -> BindResult { + let initializer = match handle.initializers.iter().find(|x| x.name == name) { + None => { + return Err(BindingError::InitializerDoesNotExist { + name, + struct_name: handle.declaration.name().clone(), + }) + } + Some(x) => x.clone(), + }; + + // all values must be initialized + if initializer.values.len() != handle.fields.len() { + return Err(BindingError::InitializerNotParameterless { + name, + struct_name: handle.declaration.name().clone(), + }); + } + + Ok(Self { + handle, + initializer, + }) + } +} + +impl UniversalStructHandle { + pub fn zero_parameter_initializer( + &self, + name: &'static str, + ) -> BindResult { + ZeroParameterStructInitializer::try_create(self.clone(), name) + } +} + +/// Like a BasicType but with values +#[derive(Debug, Clone)] +pub enum BasicValue { + Primitive(PrimitiveValue), + Duration(DurationValue), + Enum(EnumValue), +} + +impl BasicValue { + pub(crate) fn get_basic_type(&self) -> BasicType { + match self { + BasicValue::Primitive(x) => { + let pv: PrimitiveValue = *x; + let x: Primitive = pv.into(); + BasicType::Primitive(x) + } + BasicValue::Duration(x) => { + let dv: DurationValue = *x; + let dt: DurationType = dv.into(); + BasicType::Duration(dt) + } + BasicValue::Enum(x) => BasicType::Enum(x.handle.clone()), + } + } +} + /// types that can be returned from callback functions #[derive(Debug, Clone, PartialEq, Eq)] pub enum CallbackReturnValue { @@ -73,6 +157,48 @@ pub enum CallbackReturnValue { Struct(UniversalStructHandle), } +/// Like CallbackReturnValue, but with a value +#[derive(Debug, Clone)] +pub enum DefaultCallbackReturnValue { + Basic(BasicValue), + InitializedStruct(ZeroParameterStructInitializer), +} + +impl DefaultCallbackReturnValue { + pub(crate) fn get_callback_return_value(&self) -> CallbackReturnValue { + match self { + DefaultCallbackReturnValue::Basic(x) => CallbackReturnValue::Basic(x.get_basic_type()), + DefaultCallbackReturnValue::InitializedStruct(x) => { + CallbackReturnValue::Struct(x.handle.clone()) + } + } + } +} + +impl From for DefaultCallbackReturnValue { + fn from(x: ZeroParameterStructInitializer) -> Self { + DefaultCallbackReturnValue::InitializedStruct(x) + } +} + +impl From for DefaultCallbackReturnValue { + fn from(x: PrimitiveValue) -> Self { + DefaultCallbackReturnValue::Basic(BasicValue::Primitive(x)) + } +} + +impl From for DefaultCallbackReturnValue { + fn from(x: DurationValue) -> Self { + DefaultCallbackReturnValue::Basic(BasicValue::Duration(x)) + } +} + +impl From for DefaultCallbackReturnValue { + fn from(x: EnumValue) -> Self { + DefaultCallbackReturnValue::Basic(BasicValue::Enum(x)) + } +} + impl From for CallbackReturnValue { fn from(x: Primitive) -> Self { Self::Basic(x.into()) @@ -134,6 +260,7 @@ where pub name: Name, pub functional_transform: FunctionalTransform, pub return_type: OptionalReturnType, + pub default_implementation: Option, pub arguments: Vec>, pub doc: Doc, } @@ -149,6 +276,7 @@ impl CallbackFunction { name: self.name.clone(), functional_transform: self.functional_transform, return_type: self.return_type.validate(&self.name, lib)?, + default_implementation: self.default_implementation.clone(), arguments: arguments?, doc: self .doc diff --git a/oo-bindgen/src/model/structs/common.rs b/oo-bindgen/src/model/structs/common.rs index f98d3a14..557e8700 100644 --- a/oo-bindgen/src/model/structs/common.rs +++ b/oo-bindgen/src/model/structs/common.rs @@ -452,7 +452,7 @@ impl InitializerType { } } -// An initializer defines how to construct a struct +/// An initializer defines how to construct a struct #[derive(Debug, Clone)] pub struct Initializer where diff --git a/oo-bindgen/src/model/types.rs b/oo-bindgen/src/model/types.rs index 40987af8..f378a75b 100644 --- a/oo-bindgen/src/model/types.rs +++ b/oo-bindgen/src/model/types.rs @@ -15,6 +15,24 @@ pub enum DurationType { Seconds, } +/// Same as DurationType but with an associated value +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)] +pub enum DurationValue { + /// Duration is represented as a count of milliseconds in a u64 value + Milliseconds(u64), + /// Duration is represented as a count of seconds in a u64 value + Seconds(u64), +} + +impl From for DurationType { + fn from(x: DurationValue) -> Self { + match x { + DurationValue::Milliseconds(_) => DurationType::Milliseconds, + DurationValue::Seconds(_) => DurationType::Seconds, + } + } +} + impl DurationType { pub fn unit(&self) -> &'static str { match self { @@ -95,6 +113,40 @@ pub enum Primitive { Double, } +/// same as primitive, but with a value +#[derive(Debug, Copy, Clone)] +pub enum PrimitiveValue { + Bool(bool), + U8(u8), + S8(i8), + U16(u16), + S16(i16), + U32(u32), + S32(i32), + U64(u64), + S64(i64), + Float(f32), + Double(f64), +} + +impl From for Primitive { + fn from(x: PrimitiveValue) -> Self { + match x { + PrimitiveValue::Bool(_) => Primitive::Bool, + PrimitiveValue::U8(_) => Primitive::U8, + PrimitiveValue::S8(_) => Primitive::S8, + PrimitiveValue::U16(_) => Primitive::U16, + PrimitiveValue::S16(_) => Primitive::S16, + PrimitiveValue::U32(_) => Primitive::U32, + PrimitiveValue::S32(_) => Primitive::S32, + PrimitiveValue::U64(_) => Primitive::U64, + PrimitiveValue::S64(_) => Primitive::S64, + PrimitiveValue::Float(_) => Primitive::Float, + PrimitiveValue::Double(_) => Primitive::Double, + } + } +} + /// Basic types are trivially copyable. They can be used /// in almost any context within the API model #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/tests/bindings/c/CMakeLists.txt b/tests/bindings/c/CMakeLists.txt index c60b5c6f..0bd939d9 100644 --- a/tests/bindings/c/CMakeLists.txt +++ b/tests/bindings/c/CMakeLists.txt @@ -31,6 +31,7 @@ set(cpp_test_files cpp_tests/callback_tests.cpp cpp_tests/collection_tests.cpp cpp_tests/constant_tests.cpp + cpp_tests/default_interface_tests.cpp cpp_tests/enum_tests.cpp cpp_tests/error_tests.cpp cpp_tests/duration_tests.cpp diff --git a/tests/bindings/c/cpp_tests/default_interface_tests.cpp b/tests/bindings/c/cpp_tests/default_interface_tests.cpp new file mode 100644 index 00000000..ebe5d73a --- /dev/null +++ b/tests/bindings/c/cpp_tests/default_interface_tests.cpp @@ -0,0 +1,16 @@ +#include + +#include "foo.hpp" + +class DefaultedInterface final : public foo::DefaultedInterface {}; + +void defaulted_interface_tests() +{ + auto instance = DefaultedInterface(); + + assert(foo::DefaultInterfaceTest::get_duration_value(instance) == std::chrono::milliseconds(42)); + assert(foo::DefaultInterfaceTest::get_u32_value(instance) == 42); + assert(foo::DefaultInterfaceTest::get_switch_pos(instance) == foo::SwitchPosition::on); + assert(foo::DefaultInterfaceTest::get_bool_value(instance)); + assert(foo::DefaultInterfaceTest::get_wrapped_number(instance).num == 42); +} diff --git a/tests/bindings/c/cpp_tests/main.cpp b/tests/bindings/c/cpp_tests/main.cpp index 2a361acb..6e6b6439 100644 --- a/tests/bindings/c/cpp_tests/main.cpp +++ b/tests/bindings/c/cpp_tests/main.cpp @@ -1,5 +1,6 @@ void version_tests(); void constant_tests(); +void defaulted_interface_tests(); void enum_tests(); void error_tests(); void duration_tests(); @@ -16,6 +17,7 @@ int main() { version_tests(); constant_tests(); + defaulted_interface_tests(); enum_tests(); error_tests(); iterator_tests(); diff --git a/tests/bindings/dotnet/foo.Tests/CallbackTest.cs b/tests/bindings/dotnet/foo.Tests/CallbackTest.cs index 3dca0345..4c2b1a6f 100644 --- a/tests/bindings/dotnet/foo.Tests/CallbackTest.cs +++ b/tests/bindings/dotnet/foo.Tests/CallbackTest.cs @@ -58,7 +58,7 @@ public class CallbackTests { [Fact] public void CallbackTest() - { + { using var cbSource = new CallbackSource(); var cb = new CallbackImpl(); diff --git a/tests/bindings/java/foo-tests/src/test/java/io/stepfunc/foo_test/DefaultInterfaceMethodTest.java b/tests/bindings/java/foo-tests/src/test/java/io/stepfunc/foo_test/DefaultInterfaceMethodTest.java new file mode 100644 index 00000000..62df4cfb --- /dev/null +++ b/tests/bindings/java/foo-tests/src/test/java/io/stepfunc/foo_test/DefaultInterfaceMethodTest.java @@ -0,0 +1,25 @@ +package io.stepfunc.foo_test; + +import io.stepfunc.foo.DefaultInterfaceTest; +import io.stepfunc.foo.DefaultedInterface; +import io.stepfunc.foo.SwitchPosition; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + + +class DefaultInterfaceMethodTest { + + static class DefaultInterfaceImpl implements DefaultedInterface {} + + @Test + void InterfaceAndOneTimeCallbackTest() { + DefaultedInterface di = new DefaultInterfaceImpl(); + Assertions.assertTrue(DefaultInterfaceTest.getBoolValue(di)); + Assertions.assertEquals(42, DefaultInterfaceTest.getI32Value(di)); + Assertions.assertEquals(Duration.ofMillis(42), DefaultInterfaceTest.getDurationValue(di)); + Assertions.assertEquals(SwitchPosition.ON, DefaultInterfaceTest.getSwitchPos(di)); + Assertions.assertEquals(42, DefaultInterfaceTest.getWrappedNumber(di).num); + } +} diff --git a/tests/foo-ffi/src/interface_defaults.rs b/tests/foo-ffi/src/interface_defaults.rs new file mode 100644 index 00000000..b8125876 --- /dev/null +++ b/tests/foo-ffi/src/interface_defaults.rs @@ -0,0 +1,23 @@ +pub(crate) fn get_bool_value(cb: crate::ffi::DefaultedInterface) -> bool { + cb.get_bool_value().unwrap() +} + +pub(crate) fn get_i32_value(cb: crate::ffi::DefaultedInterface) -> i32 { + cb.get_i32_value().unwrap() +} + +pub(crate) fn get_u32_value(cb: crate::ffi::DefaultedInterface) -> u32 { + cb.get_u32_value().unwrap() +} + +pub(crate) fn get_duration_value(cb: crate::ffi::DefaultedInterface) -> std::time::Duration { + cb.get_duration_ms().unwrap() +} + +pub(crate) fn get_switch_pos(cb: crate::ffi::DefaultedInterface) -> crate::ffi::SwitchPosition { + cb.get_switch_position().unwrap() +} + +pub(crate) fn get_wrapped_number(cb: crate::ffi::DefaultedInterface) -> crate::ffi::WrappedNumber { + cb.get_wrapped_number().unwrap() +} diff --git a/tests/foo-ffi/src/lib.rs b/tests/foo-ffi/src/lib.rs index c262990e..be857094 100644 --- a/tests/foo-ffi/src/lib.rs +++ b/tests/foo-ffi/src/lib.rs @@ -7,6 +7,7 @@ pub use duration::*; pub use enums::*; pub use error::*; pub use integer::*; +pub(crate) use interface_defaults::*; pub use iterator::*; pub use lifetime::*; pub use opaque_struct::*; @@ -23,6 +24,7 @@ mod duration; mod enums; mod error; mod integer; +mod interface_defaults; mod iterator; mod lifetime; mod opaque_struct; diff --git a/tests/foo-schema/src/interface_defaults.rs b/tests/foo-schema/src/interface_defaults.rs new file mode 100644 index 00000000..9d03c043 --- /dev/null +++ b/tests/foo-schema/src/interface_defaults.rs @@ -0,0 +1,147 @@ +use oo_bindgen::model::*; + +pub fn define(lib: &mut LibraryBuilder) -> BackTraced<()> { + let simple_struct = lib.declare_universal_struct("wrapped_number")?; + + let num_field = Name::create("num")?; + + let wrapped_number = lib + .define_universal_struct(simple_struct)? + .add(num_field.clone(), Primitive::S32, "the number")? + .doc("Wrapped around a single i32")? + .end_fields()? + .begin_initializer( + "default_value", + InitializerType::Static, + "initialize to default values", + )? + .default(&num_field, NumberValue::S32(42))? + .end_initializer()? + .build()?; + + let switch_position = lib + .define_enum("switch_position")? + .push("on", "Switch is in the ON position")? + .push("off", "Switch is in the OFF position")? + .doc("Switches can be ON or OFF!")? + .build()?; + + let default_switch_pos = switch_position.value("on")?; + + // Declare interface + let interface = lib + .define_interface("defaulted_interface", "Test interface with default methods")? + // bool + .begin_callback( + "get_bool_value", + "Retrieve a bool value from a user interface", + )? + .returns_with_default( + PrimitiveValue::Bool(true), + "Some value special value from the user or the default", + )? + .end_callback()? + // i32 + .begin_callback( + "get_i32_value", + "Retrieve an i32 value from a user interface", + )? + .returns_with_default( + PrimitiveValue::S32(42), + "Some value special value from the user or the default", + )? + .end_callback()? + // u32 + .begin_callback( + "get_u32_value", + "Retrieve an u32 value from a user interface", + )? + .returns_with_default( + PrimitiveValue::U32(42), + "Some value special value from the user or the default", + )? + .end_callback()? + // duration + .begin_callback( + "get_duration_ms", + "Retrieve a millisecond duration from the user", + )? + .returns_with_default( + DurationValue::Milliseconds(42), + "Retrieve a duration from a user interface", + )? + .end_callback()? + // enum + .begin_callback("get_switch_position", "retrieve the position of a switch")? + .returns_with_default(default_switch_pos, "The current position of the switch")? + .end_callback()? + // struct + .begin_callback( + "get_wrapped_number", + "retrieve a structure which is just a wrapped i32", + )? + .returns_with_default( + wrapped_number.zero_parameter_initializer("default_value")?, + "Wrapped number value", + )? + .end_callback()? + .build_sync()?; + + let get_bool = lib + .define_function("get_bool_value")? + .param("cb", interface.clone(), "callback interface")? + .returns(Primitive::Bool, "value retrieved from interface")? + .doc("retrieve value from interface")? + .build_static("get_bool_value")?; + + let get_u32 = lib + .define_function("get_u32_value")? + .param("cb", interface.clone(), "callback interface")? + .returns(Primitive::U32, "value retrieved from interface")? + .doc("retrieve value from interface")? + .build_static("get_u32_value")?; + + let get_i32 = lib + .define_function("get_i32_value")? + .param("cb", interface.clone(), "callback interface")? + .returns(Primitive::S32, "value retrieved from interface")? + .doc("retrieve value from interface")? + .build_static("get_i32_value")?; + + let get_duration = lib + .define_function("get_duration_value")? + .param("cb", interface.clone(), "callback interface")? + .returns(DurationType::Milliseconds, "value retrieved from interface")? + .doc("retrieve value from interface")? + .build_static("get_duration_value")?; + + let get_switch_pos = lib + .define_function("get_switch_pos")? + .param("cb", interface.clone(), "callback interface")? + .returns(switch_position, "current position of the switch")? + .doc("retrieve the current position of the switch")? + .build_static("get_switch_pos")?; + + let get_wrapped_number = lib + .define_function("get_wrapped_number")? + .param("cb", interface, "callback interface")? + .returns( + wrapped_number, + "wrapped number retrieved from the interface", + )? + .doc("retrieve the current wrapped number value")? + .build_static("get_wrapped_number")?; + + // Define the class + lib.define_static_class("default_interface_test")? + .static_method(get_bool)? + .static_method(get_u32)? + .static_method(get_i32)? + .static_method(get_duration)? + .static_method(get_switch_pos)? + .static_method(get_wrapped_number)? + .doc("Class that demonstrate the usage of an async interface")? + .build()?; + + Ok(()) +} diff --git a/tests/foo-schema/src/lib.rs b/tests/foo-schema/src/lib.rs index 756ec5ec..fc2dbb78 100644 --- a/tests/foo-schema/src/lib.rs +++ b/tests/foo-schema/src/lib.rs @@ -10,6 +10,7 @@ mod duration; mod enums; mod error; mod integer; +mod interface_defaults; mod iterator; mod lifetime; mod opaque_struct; @@ -67,6 +68,7 @@ pub fn build_lib() -> BackTraced { enums::define(&mut builder)?; error::define(&mut builder)?; integer::define(&mut builder)?; + interface_defaults::define(&mut builder)?; iterator::define(&mut builder)?; opaque_struct::define(&mut builder)?; primitive_iterator::define(&mut builder)?;