use std::{path::PathBuf, sync::{Arc, mpsc}, io}; use neon::{prelude::*, result::Throw}; use once_cell::sync::OnceCell; use thiserror::Error; use veilid_core::{CryptoKind, CryptoTyped, KeyPair, CRYPTO_KIND_VLD0, VeilidAPI, api_startup_json, VeilidAPIError}; // #[derive(Error, Debug)] // pub enum BindingsError { // #[error(transparent)] // VeilidAPIError (#[from] VeilidAPIError), // #[error(transparent)] // Throw (#[from] Throw) // } fn make_runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static tokio::runtime::Runtime> { static RUNTIME: OnceCell = OnceCell::new(); RUNTIME.get_or_try_init(|| tokio::runtime::Runtime::new() .or_else(|error| cx.throw_error(error.to_string())) ) } // fn hello(mut cx: FunctionContext) -> JsResult { // Ok(cx.string(veilid_core::veilid_version_string())) // } // fn async_hello(mut cx: FunctionContext) -> JsResult { // let runtime = make_runtime(&mut cx)?; // let (deferred, promise) = cx.promise(); // let channel = cx.channel(); // runtime.spawn(async move { // let result = veilid_core::veilid_version_string(); // deferred.settle_with(&channel, |mut cx| { // Ok(cx.string(result)) // }); // }); // Ok(promise) // } // CryptoKind #[derive(Clone, Copy)] struct NoFinalize(V); impl Finalize for NoFinalize { fn finalize<'a, C: Context<'a>>(self, _: &mut C) { // nothing to do } } impl NoFinalize { fn unbox(self) -> V { self.0 } } // trait FunctionContextExt { // fn simple_boxed(&mut self, value: U) -> Handle>>; // } // impl<'a> FunctionContextExt for FunctionContext<'a> { // fn simple_boxed(&mut self, value: U) -> Handle>> { // self.boxed(NoFinalize(value)) // } // } type BoxedKeypair = JsBox>>; type BoxedCryptoKind = JsBox>; type BoxedPathBuf = JsBox>; type BoxedLibraryCore = JsBox; // Type utility functions fn make_path(mut cx: FunctionContext) -> JsResult { let path_string = cx.argument ::(0)? .value(&mut cx); Ok(cx.boxed(NoFinalize(PathBuf::from(path_string)))) } // Library API fn crypto_generate_keypair(mut cx: FunctionContext) -> JsResult { let crypto_kind = cx.argument ::(0)? .unbox(); let keypair = veilid_core::Crypto::generate_keypair(crypto_kind) .or_else(|err| cx.throw_error(err.to_string()))?; Ok(cx.boxed(NoFinalize(keypair))) } #[derive(Debug)] struct LibraryCore { api: VeilidAPI } impl Finalize for LibraryCore { fn finalize<'a, C: Context<'a>>(self, _: &mut C) { // FIXME } } impl LibraryCore { fn from_json(mut cx: FunctionContext,) -> JsResult { // 0: update_callback // 1: json_config let update_callback = cx.argument ::(0)? .root(&mut cx); // To allow sending to a different thread let json_config = cx.argument ::(1)? .value(&mut cx); let runtime = make_runtime(&mut cx)?; let (deferred, promise) = cx.promise(); let startup_channel = cx.channel(); let (update_sender, update_receiver) = flume::unbounded(); // let _startup_task = runtime.spawn(async move { let update_handler = Arc::new(move |update| { let result = update_sender.send(update); if let Err(error) = result { // FIXME: Don't just dump this to console! println!("failed to send veilid update from callback: {:?}", error.into_inner()); } }); let core_result = async { let api = api_startup_json(update_handler, json_config).await?; let core = LibraryCore { api: api }; Ok(core) }.await; deferred.settle_with(&startup_channel, |mut cx| { let core = core_result.or_else(|err: VeilidAPIError| cx.throw_error(err.to_string()))?; let object = cx.empty_object(); let _ref = cx.boxed(core); object.set(&mut cx, "_ref", _ref)?; let test = JsFunction::new(&mut cx, LibraryCore::test)?; object.set(&mut cx, "test", test)?; let test2 = JsFunction::new(&mut cx, LibraryCore::test2)?; object.set(&mut cx, "test2", test2)?; Ok(object) }); }); let update_channel = cx.channel(); let _calling_task = runtime.spawn(async move { loop { let update = update_receiver.recv_async().await; let update_callback = update_callback.clone(&mut cx); update_channel.send(move |mut cx| { update_callback .into_inner(&mut cx) // Unpack original callback .call_with(&mut cx) .arg(cx.string(format!("{:?}", update))) .apply::(&mut cx); Ok(()) }).join(); // FIXME: This will probably block? } }); Ok(promise) } fn test(mut cx: FunctionContext) -> JsResult { let this = cx.this() .get_value(&mut cx, "_ref")? .downcast_or_throw::(&mut cx)?; println!("{:?}", this); Ok(cx.string(format!("{:?}", &this.api))) } fn test2(mut cx: FunctionContext) -> JsResult { let this = cx.this() .get_value(&mut cx, "_ref")? .downcast_or_throw::(&mut cx)?; println!("{:?}", this); Ok(cx.string(format!("{:?}", &this.api))) } } #[neon::main] fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("makePath", make_path)?; cx.export_function("crypto_generateKeypair", crypto_generate_keypair)?; cx.export_function("libraryCore_fromJSON", LibraryCore::from_json)?; let crypto_kind_vld0 = cx.boxed(NoFinalize(CRYPTO_KIND_VLD0)); cx.export_value("CRYPTO_KIND_VLD0", crypto_kind_vld0)?; Ok(()) }