You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
5.5 KiB
Rust

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<tokio::runtime::Runtime> = 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<JsString> {
// Ok(cx.string(veilid_core::veilid_version_string()))
// }
// fn async_hello(mut cx: FunctionContext) -> JsResult<JsPromise> {
// 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>(V);
impl<V> Finalize for NoFinalize<V> {
fn finalize<'a, C: Context<'a>>(self, _: &mut C) {
// nothing to do
}
}
impl<V> NoFinalize<V> {
fn unbox(self) -> V {
self.0
}
}
// trait FunctionContextExt {
// fn simple_boxed<U: Send>(&mut self, value: U) -> Handle<JsBox<NoFinalize<U>>>;
// }
// impl<'a> FunctionContextExt for FunctionContext<'a> {
// fn simple_boxed<U: Send>(&mut self, value: U) -> Handle<JsBox<NoFinalize<U>>> {
// self.boxed(NoFinalize(value))
// }
// }
type BoxedKeypair = JsBox<NoFinalize<CryptoTyped<KeyPair>>>;
type BoxedCryptoKind = JsBox<NoFinalize<CryptoKind>>;
type BoxedPathBuf = JsBox<NoFinalize<PathBuf>>;
type BoxedLibraryCore = JsBox<LibraryCore>;
// Type utility functions
fn make_path(mut cx: FunctionContext) -> JsResult<BoxedPathBuf> {
let path_string = cx.argument
::<JsString>(0)?
.value(&mut cx);
Ok(cx.boxed(NoFinalize(PathBuf::from(path_string))))
}
// Library API
fn crypto_generate_keypair(mut cx: FunctionContext) -> JsResult<BoxedKeypair> {
let crypto_kind = cx.argument
::<BoxedCryptoKind>(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<JsPromise> {
// 0: update_callback
// 1: json_config
let update_callback = cx.argument
::<JsFunction>(0)?
.root(&mut cx); // To allow sending to a different thread
let json_config = cx.argument
::<JsString>(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::<JsUndefined, _>(&mut cx);
Ok(())
}).join(); // FIXME: This will probably block?
}
});
Ok(promise)
}
fn test(mut cx: FunctionContext) -> JsResult<JsString> {
let this = cx.this()
.get_value(&mut cx, "_ref")?
.downcast_or_throw::<BoxedLibraryCore, _>(&mut cx)?;
println!("{:?}", this);
Ok(cx.string(format!("{:?}", &this.api)))
}
fn test2(mut cx: FunctionContext) -> JsResult<JsString> {
let this = cx.this()
.get_value(&mut cx, "_ref")?
.downcast_or_throw::<BoxedLibraryCore, _>(&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(())
}