diff --git a/Cargo.toml b/Cargo.toml index b06e6aa..d37f4f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,4 @@ members = [ "deno_bindgen_ir", "deno_bindgen_cli" ] -exclude = ["e2e_test/"] \ No newline at end of file +exclude = ["e2e_test/", "example/"] \ No newline at end of file diff --git a/Makefile b/Makefile index 6d4c5e1..49b61df 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ fmt: cargo fmt - deno fmt --ignore=target/,e2e_test/target/,e2e_test/bindings/ + deno fmt --ignore=target/,e2e_test/target/,e2e_test/bindings/,example/target/ build: cargo build diff --git a/deno_bindgen_cli/cargo.rs b/deno_bindgen_cli/cargo.rs index 13f2751..cca48fc 100644 --- a/deno_bindgen_cli/cargo.rs +++ b/deno_bindgen_cli/cargo.rs @@ -41,11 +41,12 @@ impl Build { let output = cmd.output()?; if status.success() { let reader = std::io::BufReader::new(output.stdout.as_slice()); + let mut artifacts = vec![]; for message in cargo_metadata::Message::parse_stream(reader) { match message.unwrap() { cargo_metadata::Message::CompilerArtifact(artifact) => { if artifact.target.kind.contains(&"cdylib".to_string()) { - return Ok(Artifact { + artifacts.push(Artifact { path: PathBuf::from(artifact.filenames[0].to_string()), manifest_path: PathBuf::from( artifact.manifest_path.to_string(), @@ -57,6 +58,12 @@ impl Build { } } + // TODO: Fix. Not an ideal way to get the artifact of the desired crate, but it + // works for most case. + if let Some(artifact) = artifacts.pop() { + return Ok(artifact); + } + Err(std::io::Error::new( std::io::ErrorKind::Other, "failed to parse cargo output", @@ -72,9 +79,8 @@ impl Build { } } -pub fn metadata(path: &Path) -> Result { +pub fn metadata() -> Result { let metadata = cargo_metadata::MetadataCommand::new() - .manifest_path(path) .exec() .map_err(|e| { println!("failed to execute `cargo metadata`: {}", e); diff --git a/deno_bindgen_cli/main.rs b/deno_bindgen_cli/main.rs index 2ac78ee..808d762 100644 --- a/deno_bindgen_cli/main.rs +++ b/deno_bindgen_cli/main.rs @@ -24,16 +24,16 @@ fn main() -> std::io::Result<()> { let opt = Opt::from_args(); let cwd = std::env::current_dir().unwrap(); - let Artifact { - path, - manifest_path, - } = cargo::Build::new().release(opt.release).build(&cwd)?; + let Artifact { path, .. } = + cargo::Build::new().release(opt.release).build(&cwd)?; - let name = cargo::metadata(&manifest_path)?; + let name = cargo::metadata()?; println!("Initializing {name}"); unsafe { dlfcn::load_and_init(&PathBuf::from(path), opt.out, opt.lazy_init)? }; + + println!("Ready {name}"); Ok(()) } diff --git a/e2e_test/bench.js b/e2e_test/bench.js index ed45ae9..10d472c 100644 --- a/e2e_test/bench.js +++ b/e2e_test/bench.js @@ -1,6 +1,12 @@ -import { add, bytelen } from "./bindings/bindings.ts"; +import { add, bytelen, Foo, make_foo } from "./bindings/mod.ts"; Deno.bench("add", () => add(1, 2)); const b = new Uint8Array([1, 2, 3, 4]); Deno.bench("bytelen", () => bytelen(b)); + +Deno.bench("make_foo", () => make_foo(21)); +Deno.bench("new Foo", () => new Foo(21)); + +const foo = new Foo(21); +Deno.bench("Foo#bar", () => foo.bar(1)); diff --git a/e2e_test/bindings/mod.ts b/e2e_test/bindings/mod.ts index df8b54e..4071862 100644 --- a/e2e_test/bindings/mod.ts +++ b/e2e_test/bindings/mod.ts @@ -5,115 +5,112 @@ const { dlopen } = Deno; -const { symbols } = dlopen( - "/Users/divy/gh/deno_bindgen/e2e_test/target/debug/libdeno_bindgen_e2e.dylib", - { - add: { - parameters: [ - "i32", - "i32", - ], - result: "i32", - nonblocking: false, - }, - __Input_new: { - parameters: [ - "i32", - "i32", - ], - result: "pointer", - nonblocking: false, - }, - __Input_dealloc: { - parameters: [ - "pointer", - ], - result: "void", - nonblocking: false, - }, - add2: { - parameters: [ - "pointer", - ], - result: "i32", - nonblocking: false, - }, - bytelen: { - parameters: [ - "buffer", - "usize", - ], - result: "u32", - nonblocking: false, - }, - buf_mut: { - parameters: [ - "buffer", - "usize", - ], - result: "void", - nonblocking: false, - }, - cstr: { - parameters: [], - result: "pointer", - nonblocking: false, - }, - strlen: { - parameters: [ - "pointer", - ], - result: "u32", - nonblocking: false, - }, - non_blocking: { - parameters: [], - result: "i32", - nonblocking: true, - }, - make_foo: { - parameters: [], - result: "pointer", - nonblocking: false, - }, - inc_foo: { - parameters: [ - "pointer", - ], - result: "void", - nonblocking: false, - }, - __Foo_new: { - parameters: [ - "u32", - ], - result: "pointer", - nonblocking: false, - }, - __Foo_inc: { - parameters: [ - "pointer", - ], - result: "void", - nonblocking: false, - }, - __Foo_bar: { - parameters: [ - "pointer", - "u32", - ], - result: "u32", - nonblocking: false, - }, - __Foo_dealloc: { - parameters: [ - "pointer", - ], - result: "void", - nonblocking: false, - }, +const { symbols } = dlopen('/Users/divy/gh/deno_bindgen/e2e_test/target/debug/libdeno_bindgen_e2e.dylib', { + add: { + parameters: [ + 'i32', + 'i32', + ], + result: 'i32', + nonblocking: false }, -); + __Input_new: { + parameters: [ + 'i32', + 'i32', + ], + result: 'pointer', + nonblocking: false + }, + __Input_dealloc: { + parameters: [ + 'pointer', + ], + result: 'void', + nonblocking: false + }, + add2: { + parameters: [ + 'pointer', + ], + result: 'i32', + nonblocking: false + }, + bytelen: { + parameters: [ + 'buffer', + 'usize', + ], + result: 'u32', + nonblocking: false + }, + buf_mut: { + parameters: [ + 'buffer', + 'usize', + ], + result: 'void', + nonblocking: false + }, + cstr: { + parameters: [], + result: 'pointer', + nonblocking: false + }, + strlen: { + parameters: [ + 'pointer', + ], + result: 'u32', + nonblocking: false + }, + non_blocking: { + parameters: [], + result: 'i32', + nonblocking: true + }, + make_foo: { + parameters: [], + result: 'pointer', + nonblocking: false + }, + inc_foo: { + parameters: [ + 'pointer', + ], + result: 'void', + nonblocking: false + }, + __Foo_new: { + parameters: [ + 'u32', + ], + result: 'pointer', + nonblocking: false + }, + __Foo_inc: { + parameters: [ + 'pointer', + ], + result: 'void', + nonblocking: false + }, + __Foo_bar: { + parameters: [ + 'pointer', + 'u32', + ], + result: 'u32', + nonblocking: false + }, + __Foo_dealloc: { + parameters: [ + 'pointer', + ], + result: 'void', + nonblocking: false + }, +}); export function add( arg0: number, @@ -122,7 +119,7 @@ export function add( return symbols.add( arg0, arg1, - ); + ) } function __Input_new( @@ -132,7 +129,7 @@ function __Input_new( const ret = symbols.__Input_new( arg0, arg1, - ); + ) return Input.__constructor(ret); } @@ -141,7 +138,7 @@ function __Input_dealloc( ): void { return symbols.__Input_dealloc( arg0, - ); + ) } export class Input { @@ -162,13 +159,13 @@ export class Input { return __Input_new( arg0, arg1, - ); + ) } dealloc(): void { return __Input_dealloc( this.ptr, - ); + ) } } @@ -177,7 +174,7 @@ export function add2( ): number { return symbols.add2( arg0.ptr, - ); + ) } export function bytelen( @@ -186,7 +183,7 @@ export function bytelen( return symbols.bytelen( arg0, arg0.byteLength, - ); + ) } export function buf_mut( @@ -195,11 +192,11 @@ export function buf_mut( return symbols.buf_mut( arg0, arg0.byteLength, - ); + ) } export function cstr(): Deno.PointerObject | null { - return symbols.cstr(); + return symbols.cstr() } export function strlen( @@ -207,15 +204,15 @@ export function strlen( ): number { return symbols.strlen( arg0, - ); + ) } export function non_blocking(): Promise { - return symbols.non_blocking(); + return symbols.non_blocking() } export function make_foo(): Foo { - const ret = symbols.make_foo(); + const ret = symbols.make_foo() return Foo.__constructor(ret); } @@ -224,7 +221,7 @@ export function inc_foo( ): void { return symbols.inc_foo( arg0.ptr, - ); + ) } function __Foo_new( @@ -232,7 +229,7 @@ function __Foo_new( ): Foo { const ret = symbols.__Foo_new( arg0, - ); + ) return Foo.__constructor(ret); } @@ -241,7 +238,7 @@ function __Foo_inc( ): void { return symbols.__Foo_inc( arg0, - ); + ) } function __Foo_bar( @@ -251,7 +248,7 @@ function __Foo_bar( return symbols.__Foo_bar( arg0, arg1, - ); + ) } function __Foo_dealloc( @@ -259,7 +256,7 @@ function __Foo_dealloc( ): void { return symbols.__Foo_dealloc( arg0, - ); + ) } export class Foo { @@ -279,25 +276,26 @@ export class Foo { constructor(arg0: number) { return __Foo_new( arg0, - ); + ) } inc(): void { return __Foo_inc( this.ptr, - ); + ) } bar(arg0: number): number { return __Foo_bar( this.ptr, arg0, - ); + ) } dealloc(): void { return __Foo_dealloc( this.ptr, - ); + ) } } + diff --git a/example/Cargo.toml b/example/Cargo.toml new file mode 100644 index 0000000..9b7aada --- /dev/null +++ b/example/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "usb_example" +version = "0.1.0" +edition = "2018" + +[dependencies] +deno_bindgen = { path = "../deno_bindgen/" } +webusb = "0.5.0" +serde = { version = "1", features = ["derive"] } +linkme = "0.3" + +[lib] +name = "deno_usb" +path = "./lib.rs" +crate-type = ["cdylib"] diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..4435c77 --- /dev/null +++ b/example/README.md @@ -0,0 +1,13 @@ +## `deno_usb_example` + +```bash +$> deno_bindgen -o mod.ts + Finished dev [unoptimized + debuginfo] target(s) in 0.02s +Initializing usb_example +Ready usb_example + +$> deno run --allow-ffi --unstable lsusb.ts +Product Name: G102 LIGHTSYNC Gaming Mouse +Vendor ID: 1133 +Product ID: 49298 +``` diff --git a/example/lib.rs b/example/lib.rs new file mode 100644 index 0000000..6620944 --- /dev/null +++ b/example/lib.rs @@ -0,0 +1,71 @@ +use deno_bindgen::deno_bindgen; + +#[deno_bindgen] +pub struct Context { + context: webusb::Context, +} + +#[deno_bindgen] +impl Context { + #[constructor] + pub fn init() -> Context { + let context = webusb::Context::init().expect("Unable to create context"); + Context { context } + } + + pub fn lsusb(&self) { + let devices = self.context.devices().expect("Unable to get devices"); + for device in devices { + if let Some(name) = device.product_name { + println!("Product Name: {}", name); + } + + println!("Vendor ID: {}", device.vendor_id); + println!("Product ID: {}\n", device.product_id); + } + } + + pub fn open(&mut self, vendor_id: u16, product_id: u16) -> Device { + let devices = self.context.devices().expect("Unable to get devices"); + let mut device = devices + .into_iter() + .find(|d| d.vendor_id == vendor_id && d.product_id == product_id) + .expect("Device not found."); + + device.open().expect("Unable to open device."); + + Device { device } + } +} + +#[deno_bindgen] +pub struct Device { + device: webusb::UsbDevice, +} + +impl Drop for Device { + fn drop(&mut self) { + self.device.close().expect("Unable to close device."); + } +} + +#[deno_bindgen] +impl Device { + pub fn claim_interface(&mut self, interface_number: u8) { + self + .device + .claim_interface(interface_number) + .expect("Unable to claim interface."); + } + + pub fn select_alternate_interface( + &mut self, + interface_number: u8, + alternate_setting: u8, + ) { + self + .device + .select_alternate_interface(interface_number, alternate_setting) + .expect("Unable to select alternate interface."); + } +} diff --git a/example/lsusb.ts b/example/lsusb.ts new file mode 100644 index 0000000..061c8c1 --- /dev/null +++ b/example/lsusb.ts @@ -0,0 +1,4 @@ +import { Context } from "./mod.ts"; + +const context = new Context(); +context.lsusb(); diff --git a/example/mod.ts b/example/mod.ts new file mode 100644 index 0000000..43aa009 --- /dev/null +++ b/example/mod.ts @@ -0,0 +1,203 @@ +// deno-lint-ignore-file + +// This file is automatically generated by deno_bindgen. +// Do not edit this file directly. + +const { dlopen } = Deno; + +const { symbols } = dlopen( + "/Users/divy/gh/deno_bindgen/example/target/debug/libdeno_usb.dylib", + { + __Context_init: { + parameters: [], + result: "pointer", + nonblocking: false, + }, + __Context_lsusb: { + parameters: [ + "pointer", + ], + result: "void", + nonblocking: false, + }, + __Context_open: { + parameters: [ + "pointer", + "u16", + "u16", + ], + result: "pointer", + nonblocking: false, + }, + __Context_dealloc: { + parameters: [ + "pointer", + ], + result: "void", + nonblocking: false, + }, + __Device_claim_interface: { + parameters: [ + "pointer", + "u8", + ], + result: "void", + nonblocking: false, + }, + __Device_select_alternate_interface: { + parameters: [ + "pointer", + "u8", + "u8", + ], + result: "void", + nonblocking: false, + }, + __Device_dealloc: { + parameters: [ + "pointer", + ], + result: "void", + nonblocking: false, + }, + }, +); + +function __Context_init(): Context { + const ret = symbols.__Context_init(); + return Context.__constructor(ret); +} + +function __Context_lsusb( + arg0: Deno.PointerObject | null, +): void { + return symbols.__Context_lsusb( + arg0, + ); +} + +function __Context_open( + arg0: Deno.PointerObject | null, + arg1: number, + arg2: number, +): Device { + const ret = symbols.__Context_open( + arg0, + arg1, + arg2, + ); + return Device.__constructor(ret); +} + +function __Context_dealloc( + arg0: Deno.PointerObject | null, +): void { + return symbols.__Context_dealloc( + arg0, + ); +} + +export class Context { + ptr: Deno.PointerObject | null = null; + + static __constructor(ptr: Deno.PointerObject | null) { + const self = Object.create(Context.prototype); + self.ptr = ptr; + return self; + } + + [Symbol.dispose]() { + this.dealloc(); + this.ptr = null; + } + + constructor() { + return __Context_init(); + } + + lsusb(): void { + return __Context_lsusb( + this.ptr, + ); + } + + open(arg0: number, arg1: number): Device { + return __Context_open( + this.ptr, + arg0, + arg1, + ); + } + + dealloc(): void { + return __Context_dealloc( + this.ptr, + ); + } +} + +function __Device_claim_interface( + arg0: Deno.PointerObject | null, + arg1: number, +): void { + return symbols.__Device_claim_interface( + arg0, + arg1, + ); +} + +function __Device_select_alternate_interface( + arg0: Deno.PointerObject | null, + arg1: number, + arg2: number, +): void { + return symbols.__Device_select_alternate_interface( + arg0, + arg1, + arg2, + ); +} + +function __Device_dealloc( + arg0: Deno.PointerObject | null, +): void { + return symbols.__Device_dealloc( + arg0, + ); +} + +export class Device { + ptr: Deno.PointerObject | null = null; + + static __constructor(ptr: Deno.PointerObject | null) { + const self = Object.create(Device.prototype); + self.ptr = ptr; + return self; + } + + [Symbol.dispose]() { + this.dealloc(); + this.ptr = null; + } + + claim_interface(arg0: number): void { + return __Device_claim_interface( + this.ptr, + arg0, + ); + } + + select_alternate_interface(arg0: number, arg1: number): void { + return __Device_select_alternate_interface( + this.ptr, + arg0, + arg1, + ); + } + + dealloc(): void { + return __Device_dealloc( + this.ptr, + ); + } +}