diff --git a/Cargo.lock b/Cargo.lock index c089b224..35ce4de4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,15 +125,6 @@ dependencies = [ "log", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.53" @@ -707,13 +698,9 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term", - "atty", "bitflags", - "strsim 0.8.0", "textwrap 0.11.0", "unicode-width", - "vec_map", ] [[package]] @@ -726,7 +713,7 @@ dependencies = [ "bitflags", "indexmap", "os_str_bytes", - "strsim 0.10.0", + "strsim", "termcolor", "textwrap 0.14.2", ] @@ -1148,7 +1135,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim", "syn", ] @@ -1399,6 +1386,16 @@ dependencies = [ "instant", ] +[[package]] +name = "ffi-support" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6" +dependencies = [ + "lazy_static", + "log", +] + [[package]] name = "fixed-hash" version = "0.7.0" @@ -1427,26 +1424,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "flutter_rust_bridge" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ad509fe9fd3ac5a4d10a30a7de9224b74192c65c22338cc958ab0f93f0c834" -dependencies = [ - "allo-isolate", - "anyhow", - "flutter_rust_bridge_macros", - "lazy_static", - "parking_lot 0.11.2", - "threadpool", -] - -[[package]] -name = "flutter_rust_bridge_macros" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a2555456c9c12c5982543c61d79267327d522eaa1490ef4914f5ed87b16fe1" - [[package]] name = "fnv" version = "1.0.7" @@ -1900,8 +1877,8 @@ dependencies = [ "android_logger", "backtrace", "byteorder", - "cfg-if 0.1.10", - "clap 2.34.0", + "cfg-if 1.0.0", + "clap 3.0.13", "core-foundation 0.9.2", "core-foundation-sys 0.8.3", "directories", @@ -1912,7 +1889,6 @@ dependencies = [ "log", "ndk", "ndk-glue", - "owning_ref", "rpassword", "secret-service", "security-framework", @@ -3518,12 +3494,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -3644,15 +3614,6 @@ dependencies = [ "syn", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "time" version = "0.1.43" @@ -3871,12 +3832,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "veilid-cli" version = "0.1.0" @@ -3990,13 +3945,26 @@ dependencies = [ name = "veilid-flutter" version = "0.1.0" dependencies = [ + "allo-isolate", "anyhow", "async-std", + "backtrace", "cfg-if 1.0.0", - "flutter_rust_bridge", + "console_error_panic_hook", + "ffi-support", + "futures", + "js-sys", + "lazy_static", "log", "parking_lot 0.12.0", + "serde 1.0.136", + "serde_json", "veilid-core", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "wasm-logger", + "wee_alloc", ] [[package]] @@ -4030,6 +3998,22 @@ dependencies = [ "windows-service", ] +[[package]] +name = "veilid-wasm" +version = "0.1.0" +dependencies = [ + "cfg-if 1.0.0", + "console_error_panic_hook", + "js-sys", + "log", + "veilid-core", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "wasm-logger", + "wee_alloc", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index d02c3aaf..6880d9bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ members = [ "veilid-core", "veilid-server", "veilid-cli", - "veilid-flutter/rust" + "veilid-flutter/rust", + "veilid-wasm", ] exclude = [ "./external/keyring-rs", "./external/netlink", "./external/cursive" ] diff --git a/external/keyring-manager b/external/keyring-manager index 3713e105..935ca957 160000 --- a/external/keyring-manager +++ b/external/keyring-manager @@ -1 +1 @@ -Subproject commit 3713e10599c6078e058aab785ef46594420dc11b +Subproject commit 935ca957d7e223ef560a0b20b656730a325e0ba7 diff --git a/veilid-core/src/api_logger.rs b/veilid-core/src/api_logger.rs index 475b4844..5bc896d5 100644 --- a/veilid-core/src/api_logger.rs +++ b/veilid-core/src/api_logger.rs @@ -1,6 +1,6 @@ +use crate::core_context::*; use crate::intf::*; use crate::veilid_api::*; -use crate::veilid_core::*; use crate::xx::*; use log::{set_boxed_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record}; use once_cell::sync::OnceCell; @@ -23,20 +23,12 @@ impl ApiLogger { fn new_inner(level: LevelFilter, update_callback: UpdateCallback) -> ApiLoggerInner { let (tx, rx) = async_channel::unbounded::<(VeilidLogLevel, String)>(); let _join_handle: JoinHandle<()> = spawn(async move { - loop { - match rx.recv().await { - Ok(v) => { - (update_callback)(VeilidUpdate::Log { - log_level: v.0, - message: v.1, - }) - .await; - } - Err(_) => { - // Nothing to be done here... - break; - } - } + while let Ok(v) = rx.recv().await { + (update_callback)(VeilidUpdate::Log { + log_level: v.0, + message: v.1, + }) + .await; } }); ApiLoggerInner { diff --git a/veilid-core/src/attachment_manager.rs b/veilid-core/src/attachment_manager.rs index bc5e939f..60f3448a 100644 --- a/veilid-core/src/attachment_manager.rs +++ b/veilid-core/src/attachment_manager.rs @@ -6,9 +6,10 @@ use crate::xx::*; use crate::*; use core::convert::TryFrom; use core::fmt; +use serde::*; state_machine! { - derive(Debug, PartialEq, Eq, Clone, Copy) + derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize) pub Attachment(Detached) //--- Detached(AttachRequested) => Attaching [StartAttachment], @@ -102,8 +103,6 @@ impl TryFrom for AttachmentState { pub struct AttachmentManagerInner { config: VeilidConfig, - table_store: TableStore, - crypto: Crypto, attachment_machine: CallbackStateMachine, network_manager: NetworkManager, maintain_peers: bool, @@ -125,8 +124,6 @@ impl AttachmentManager { ) -> AttachmentManagerInner { AttachmentManagerInner { config: config.clone(), - table_store: table_store.clone(), - crypto: crypto.clone(), attachment_machine: CallbackStateMachine::new(), network_manager: NetworkManager::new(config, table_store, crypto), maintain_peers: false, @@ -145,14 +142,6 @@ impl AttachmentManager { self.inner.lock().config.clone() } - pub fn table_store(&self) -> TableStore { - self.inner.lock().table_store.clone() - } - - pub fn crypto(&self) -> Crypto { - self.inner.lock().crypto.clone() - } - pub fn network_manager(&self) -> NetworkManager { self.inner.lock().network_manager.clone() } @@ -274,20 +263,26 @@ impl AttachmentManager { &self, state_change_callback: StateChangeCallback, ) -> Result<(), String> { - let inner = self.inner.lock(); - inner - .attachment_machine - .set_state_change_callback(state_change_callback); + let network_manager = { + let inner = self.inner.lock(); + inner + .attachment_machine + .set_state_change_callback(state_change_callback); + inner.network_manager.clone() + }; - inner.network_manager.init().await?; + network_manager.init().await?; Ok(()) } pub async fn terminate(&self) { // Ensure we detached self.detach().await; - let inner = self.inner.lock(); - inner.network_manager.terminate().await; + let network_manager = { + let inner = self.inner.lock(); + inner.network_manager.clone() + }; + network_manager.terminate().await; } fn attach(&self) { diff --git a/veilid-core/src/core_context.rs b/veilid-core/src/core_context.rs new file mode 100644 index 00000000..fad355ae --- /dev/null +++ b/veilid-core/src/core_context.rs @@ -0,0 +1,197 @@ +use crate::api_logger::*; +use crate::attachment_manager::*; +use crate::dht::crypto::Crypto; +use crate::intf::*; +use crate::veilid_api::*; +use crate::veilid_config::*; +use crate::xx::*; + +cfg_if! { + if #[cfg(target_arch = "wasm32")] { + pub type UpdateCallback = Arc SystemPinBoxFuture<()>>; + } else { + pub type UpdateCallback = Arc SystemPinBoxFuture<()> + Send + Sync>; + } +} + +pub struct VeilidCoreSetup { + pub update_callback: UpdateCallback, + pub config_callback: ConfigCallback, +} + +pub struct VeilidCoreContext { + pub config: VeilidConfig, + pub protected_store: ProtectedStore, + pub table_store: TableStore, + pub block_store: BlockStore, + pub crypto: Crypto, + pub attachment_manager: AttachmentManager, +} + +impl VeilidCoreContext { + async fn new(setup: VeilidCoreSetup) -> Result { + // Start up api logging early if it's in the config + let api_log_level: VeilidConfigLogLevel = + *(setup.config_callback)("api_log_level".to_owned()) + .map_err(|e| VeilidAPIError::ParseError { + message: "Failed to get api_log_level".to_owned(), + value: e, + })? + .downcast() + .map_err(|e| VeilidAPIError::ParseError { + message: "Incorrect type for key 'api_log_level'".to_owned(), + value: format!("Invalid type: {:?}", e.type_id()), + })?; + if api_log_level != VeilidConfigLogLevel::Off { + ApiLogger::init( + api_log_level.to_level_filter(), + setup.update_callback.clone(), + ); + for ig in crate::DEFAULT_LOG_IGNORE_LIST { + ApiLogger::add_filter_ignore_str(ig); + } + } + + trace!("VeilidCoreContext::new starting"); + + cfg_if! { + if #[cfg(target_os = "android")] { + if utils::android::ANDROID_GLOBALS.lock().is_none() { + error!("Android globals are not set up"); + return Err("Android globals are not set up".to_owned()); + } + } + } + + // Set up config + trace!("VeilidCoreContext::new init config"); + let mut config = VeilidConfig::new(); + if let Err(e) = config.init(setup.config_callback).await { + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + // Set up protected store + trace!("VeilidCoreContext::new init protected store"); + let protected_store = ProtectedStore::new(config.clone()); + if let Err(e) = protected_store.init().await { + config.terminate().await; + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + // Init node id from config now that protected store is set up + if let Err(e) = config.init_node_id(protected_store.clone()).await { + protected_store.terminate().await; + config.terminate().await; + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + // Set up tablestore + trace!("VeilidCoreContext::new init table store"); + let table_store = TableStore::new(config.clone()); + if let Err(e) = table_store.init().await { + protected_store.terminate().await; + config.terminate().await; + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + // Set up crypto + trace!("VeilidCoreContext::new init crypto"); + let crypto = Crypto::new(config.clone(), table_store.clone()); + if let Err(e) = crypto.init().await { + table_store.terminate().await; + protected_store.terminate().await; + config.terminate().await; + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + // Set up block store + trace!("VeilidCoreContext::new init block store"); + let block_store = BlockStore::new(config.clone()); + if let Err(e) = block_store.init().await { + crypto.terminate().await; + table_store.terminate().await; + protected_store.terminate().await; + config.terminate().await; + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + // Set up attachment manager + trace!("VeilidCoreContext::new init attachment manager"); + let cb = setup.update_callback; + let attachment_manager = + AttachmentManager::new(config.clone(), table_store.clone(), crypto.clone()); + if let Err(e) = attachment_manager + .init(Arc::new( + move |_old_state: AttachmentState, new_state: AttachmentState| { + cb(VeilidUpdate::Attachment(new_state)) + }, + )) + .await + { + block_store.terminate().await; + crypto.terminate().await; + table_store.terminate().await; + protected_store.terminate().await; + config.terminate().await; + ApiLogger::terminate(); + return Err(VeilidAPIError::Internal(e)); + } + + Ok(VeilidCoreContext { + config, + protected_store, + table_store, + block_store, + crypto, + attachment_manager, + }) + } + + async fn shutdown(self) { + trace!("VeilidCoreContext::terminate_core_context starting"); + + self.attachment_manager.terminate().await; + self.block_store.terminate().await; + self.crypto.terminate().await; + self.table_store.terminate().await; + self.protected_store.terminate().await; + self.config.terminate().await; + + trace!("VeilidCoreContext::shutdown complete"); + ApiLogger::terminate(); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static INITIALIZED: AsyncMutex = AsyncMutex::new(false); + +pub async fn api_startup(setup: VeilidCoreSetup) -> Result { + // See if we have an API started up already + let mut initialized_lock = INITIALIZED.lock().await; + if *initialized_lock { + return Err(VeilidAPIError::AlreadyInitialized); + } + + // Create core context + let context = VeilidCoreContext::new(setup).await?; + + // Return an API object around our context + let veilid_api = VeilidAPI::new(context); + + *initialized_lock = true; + + Ok(veilid_api) +} + +pub async fn api_shutdown(context: VeilidCoreContext) { + let mut initialized_lock = INITIALIZED.lock().await; + context.shutdown().await; + *initialized_lock = false; +} diff --git a/veilid-core/src/dht/crypto.rs b/veilid-core/src/dht/crypto.rs index 5682caf6..ba71dbf8 100644 --- a/veilid-core/src/dht/crypto.rs +++ b/veilid-core/src/dht/crypto.rs @@ -94,38 +94,43 @@ impl Crypto { trace!("Crypto::init"); // make local copy of node id for easy access - let mut inner = self.inner.lock(); - let c = self.config.get(); - inner.node_id = c.network.node_id; - inner.node_id_secret = c.network.node_id_secret; + let (table_store, node_id) = { + let mut inner = self.inner.lock(); + let c = self.config.get(); + inner.node_id = c.network.node_id; + inner.node_id_secret = c.network.node_id_secret; + (inner.table_store.clone(), c.network.node_id) + }; // load caches if they are valid for this node id - let mut db = inner.table_store.open("crypto_caches", 1).await?; + let mut db = table_store.open("crypto_caches", 1).await?; let caches_valid = match db.load(0, b"node_id").await? { - Some(v) => v.as_slice() == inner.node_id.bytes, + Some(v) => v.as_slice() == node_id.bytes, None => false, }; if caches_valid { if let Some(b) = db.load(0, b"dh_cache").await? { + let mut inner = self.inner.lock(); bytes_to_cache(&b, &mut inner.dh_cache); } } else { drop(db); - inner.table_store.delete("crypto_caches").await?; - db = inner.table_store.open("crypto_caches", 1).await?; - db.store(0, b"node_id", &inner.node_id.bytes).await?; + table_store.delete("crypto_caches").await?; + db = table_store.open("crypto_caches", 1).await?; + db.store(0, b"node_id", &node_id.bytes).await?; } // Schedule flushing let this = self.clone(); - inner.flush_future = Some(Box::pin(interval(60000, move || { + let flush_future = interval(60000, move || { let this = this.clone(); async move { if let Err(e) = this.flush().await { warn!("flush failed: {}", e); } } - }))); + }); + self.inner.lock().flush_future = Some(flush_future); Ok(()) } @@ -161,21 +166,23 @@ impl Crypto { }; } - fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result { + fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result { let bytes = key.to_bytes(); let compressed = cd::edwards::CompressedEdwardsY(bytes); - let point = compressed.decompress().ok_or(())?; + let point = compressed + .decompress() + .ok_or_else(fn_string!("ed25519_to_x25519_pk failed"))?; let mp = point.to_montgomery(); Ok(xd::PublicKey::from(mp.to_bytes())) } - fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result { + fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result { let exp = ed::ExpandedSecretKey::from(key); let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes(); - let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(drop)?; + let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(map_to_string)?; Ok(xd::StaticSecret::from(lowbytes)) } - pub fn cached_dh(&self, key: &DHTKey, secret: &DHTKeySecret) -> Result { + pub fn cached_dh(&self, key: &DHTKey, secret: &DHTKeySecret) -> Result { if let Some(c) = self .inner .lock() @@ -197,24 +204,12 @@ impl Crypto { /////////// // These are safe to use regardless of initialization status - pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result { + pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result { assert!(key.valid); assert!(secret.valid); - let pk_ed = match ed::PublicKey::from_bytes(&key.bytes) { - Ok(v) => v, - Err(e) => { - trace!("compute_dh error: {:?}", e); - return Err(()); - } - }; + let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(map_to_string)?; let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?; - let sk_ed = match ed::SecretKey::from_bytes(&secret.bytes) { - Ok(v) => v, - Err(e) => { - trace!("compute_dh error: {:?}", e); - return Err(()); - } - }; + let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(map_to_string)?; let sk_xd = Self::ed25519_to_x25519_sk(&sk_ed)?; Ok(sk_xd.diffie_hellman(&pk_xd).to_bytes()) } @@ -236,12 +231,13 @@ impl Crypto { nonce: &Nonce, shared_secret: &SharedSecret, associated_data: Option<&[u8]>, - ) -> Result<(), ()> { + ) -> Result<(), String> { let key = ch::Key::from(*shared_secret); let xnonce = ch::XNonce::from(*nonce); let aead = ch::XChaCha20Poly1305::new(&key); aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body) - .map_err(|e| trace!("decryption failure: {}", e)) + .map_err(map_to_string) + .map_err(logthru_crypto!()) } pub fn decrypt( @@ -249,9 +245,11 @@ impl Crypto { nonce: &Nonce, shared_secret: &SharedSecret, associated_data: Option<&[u8]>, - ) -> Result, ()> { + ) -> Result, String> { let mut out = body.to_vec(); - Self::decrypt_in_place(&mut out, nonce, shared_secret, associated_data)?; + Self::decrypt_in_place(&mut out, nonce, shared_secret, associated_data) + .map_err(map_to_string) + .map_err(logthru_crypto!())?; Ok(out) } @@ -260,13 +258,14 @@ impl Crypto { nonce: &Nonce, shared_secret: &SharedSecret, associated_data: Option<&[u8]>, - ) -> Result<(), ()> { + ) -> Result<(), String> { let key = ch::Key::from(*shared_secret); let xnonce = ch::XNonce::from(*nonce); let aead = ch::XChaCha20Poly1305::new(&key); aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body) - .map_err(|e| trace!("encryption failure: {}", e)) + .map_err(map_to_string) + .map_err(logthru_crypto!()) } pub fn encrypt( @@ -274,9 +273,11 @@ impl Crypto { nonce: &Nonce, shared_secret: &SharedSecret, associated_data: Option<&[u8]>, - ) -> Result, ()> { + ) -> Result, String> { let mut out = body.to_vec(); - Self::encrypt_in_place(&mut out, nonce, shared_secret, associated_data)?; + Self::encrypt_in_place(&mut out, nonce, shared_secret, associated_data) + .map_err(map_to_string) + .map_err(logthru_crypto!())?; Ok(out) } } diff --git a/veilid-core/src/dht/envelope.rs b/veilid-core/src/dht/envelope.rs index 01f84cab..6a838cff 100644 --- a/veilid-core/src/dht/envelope.rs +++ b/veilid-core/src/dht/envelope.rs @@ -172,7 +172,7 @@ impl Envelope { crypto: Crypto, data: &[u8], node_id_secret: &DHTKeySecret, - ) -> Result, ()> { + ) -> Result, String> { // Get DH secret let dh_secret = crypto.cached_dh(&self.sender_id, node_id_secret)?; diff --git a/veilid-core/src/intf/native/block_store.rs b/veilid-core/src/intf/native/block_store.rs index c0f4166c..7e5ae3a3 100644 --- a/veilid-core/src/intf/native/block_store.rs +++ b/veilid-core/src/intf/native/block_store.rs @@ -1,2 +1,30 @@ -//use crate::intf::*; -//use crate::xx::*; +use crate::intf::*; +use crate::*; + +struct BlockStoreInner { + // +} + +#[derive(Clone)] +pub struct BlockStore { + config: VeilidConfig, + inner: Arc>, +} + +impl BlockStore { + fn new_inner() -> BlockStoreInner { + BlockStoreInner {} + } + pub fn new(config: VeilidConfig) -> Self { + Self { + config, + inner: Arc::new(Mutex::new(Self::new_inner())), + } + } + + pub async fn init(&self) -> Result<(), String> { + Ok(()) + } + + pub async fn terminate(&self) {} +} diff --git a/veilid-core/src/intf/native/mod.rs b/veilid-core/src/intf/native/mod.rs index 150dc1a8..dc25af60 100644 --- a/veilid-core/src/intf/native/mod.rs +++ b/veilid-core/src/intf/native/mod.rs @@ -2,7 +2,7 @@ mod block_store; mod network; mod protected_store; mod system; -pub mod table_store; +mod table_store; pub mod utils; pub use block_store::*; diff --git a/veilid-core/src/intf/native/protected_store.rs b/veilid-core/src/intf/native/protected_store.rs index 8677dccd..28b5b1d3 100644 --- a/veilid-core/src/intf/native/protected_store.rs +++ b/veilid-core/src/intf/native/protected_store.rs @@ -44,8 +44,8 @@ impl ProtectedStore { } pub async fn init(&self) -> Result<(), String> { - let c = self.config.get(); - { + let delete = { + let c = self.config.get(); let mut inner = self.inner.lock(); if !c.protected_store.always_use_insecure_storage { cfg_if! { @@ -74,9 +74,10 @@ impl ProtectedStore { if inner.keyring_manager.is_none() { return Err("Could not initialize the protected store.".to_owned()); } - } + c.protected_store.delete + }; - if c.protected_store.delete { + if delete { self.delete_all().await?; } diff --git a/veilid-core/src/intf/native/utils/android/mod.rs b/veilid-core/src/intf/native/utils/android/mod.rs index 4b5b9d61..8936317c 100644 --- a/veilid-core/src/intf/native/utils/android/mod.rs +++ b/veilid-core/src/intf/native/utils/android/mod.rs @@ -24,29 +24,6 @@ lazy_static! { } pub fn veilid_core_setup_android_no_log<'a>(env: JNIEnv<'a>, ctx: JObject<'a>) { - panic::set_hook(Box::new(|panic_info| { - let bt = Backtrace::new(); - if let Some(location) = panic_info.location() { - error!( - "panic occurred in file '{}' at line {}", - location.file(), - location.line(), - ); - } else { - error!("panic occurred but can't get location information..."); - } - if let Some(s) = panic_info.payload().downcast_ref::<&str>() { - error!("panic payload: {:?}", s); - } else if let Some(s) = panic_info.payload().downcast_ref::() { - error!("panic payload: {:?}", s); - } else if let Some(a) = panic_info.payload().downcast_ref::() { - error!("panic payload: {:?}", a); - } else { - error!("no panic payload"); - } - error!("Backtrace:\n{:?}", bt); - })); - *ANDROID_GLOBALS.lock() = Some(AndroidGlobals { vm: env.get_java_vm().unwrap(), ctx: env.new_global_ref(ctx).unwrap(), @@ -70,6 +47,29 @@ pub fn veilid_core_setup_android<'a>( ), ); + panic::set_hook(Box::new(|panic_info| { + let bt = Backtrace::new(); + if let Some(location) = panic_info.location() { + error!( + "panic occurred in file '{}' at line {}", + location.file(), + location.line(), + ); + } else { + error!("panic occurred but can't get location information..."); + } + if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + error!("panic payload: {:?}", s); + } else if let Some(s) = panic_info.payload().downcast_ref::() { + error!("panic payload: {:?}", s); + } else if let Some(a) = panic_info.payload().downcast_ref::() { + error!("panic payload: {:?}", a); + } else { + error!("no panic payload"); + } + error!("Backtrace:\n{:?}", bt); + })); + veilid_core_setup_android_no_log(env, ctx); } diff --git a/veilid-core/src/intf/wasm/block_store.rs b/veilid-core/src/intf/wasm/block_store.rs index 8b137891..9fc70c8e 100644 --- a/veilid-core/src/intf/wasm/block_store.rs +++ b/veilid-core/src/intf/wasm/block_store.rs @@ -1 +1,31 @@ +use crate::intf::*; +use crate::*; + +struct BlockStoreInner { + // +} + +#[derive(Clone)] +pub struct BlockStore { + config: VeilidConfig, + inner: Arc>, +} + +impl BlockStore { + fn new_inner() -> BlockStoreInner { + BlockStoreInner {} + } + pub fn new(config: VeilidConfig) -> Self { + Self { + config, + inner: Arc::new(Mutex::new(Self::new_inner())), + } + } + + pub async fn init(&self) -> Result<(), String> { + Ok(()) + } + + pub async fn terminate(&self) {} +} diff --git a/veilid-core/src/intf/wasm/network/protocol/ws.rs b/veilid-core/src/intf/wasm/network/protocol/ws.rs index aa2a5bc8..8f9af7ed 100644 --- a/veilid-core/src/intf/wasm/network/protocol/ws.rs +++ b/veilid-core/src/intf/wasm/network/protocol/ws.rs @@ -78,7 +78,7 @@ impl WebsocketProtocolHandler { assert!(local_address.is_none()); // Split dial info up - let (tls, scheme) = match &dial_info { + let (_tls, scheme) = match &dial_info { DialInfo::WS(_) => (false, "ws"), DialInfo::WSS(_) => (true, "wss"), _ => panic!("invalid dialinfo for WS/WSS protocol"), diff --git a/veilid-core/src/lib.rs b/veilid-core/src/lib.rs index 103541d3..953388a2 100644 --- a/veilid-core/src/lib.rs +++ b/veilid-core/src/lib.rs @@ -9,6 +9,7 @@ mod attachment_manager; mod callback_state_machine; mod connection_manager; mod connection_table; +mod core_context; mod dht; mod intf; mod lease_manager; @@ -19,16 +20,15 @@ mod routing_table; mod rpc_processor; mod veilid_api; mod veilid_config; -mod veilid_core; mod veilid_rng; #[macro_use] pub mod xx; pub use self::attachment_manager::AttachmentState; +pub use self::core_context::{api_startup, VeilidCoreSetup}; pub use self::veilid_api::*; pub use self::veilid_config::*; -pub use self::veilid_core::{VeilidCore, VeilidCoreSetup}; pub mod veilid_capnp { include!(concat!(env!("OUT_DIR"), "/proto/veilid_capnp.rs")); @@ -47,7 +47,7 @@ pub fn veilid_version() -> (u32, u32, u32) { ) } -pub static DEFAULT_LOG_IGNORE_LIST: [&'static str; 8] = [ +pub static DEFAULT_LOG_IGNORE_LIST: [&str; 8] = [ "async_std", "async_io", "polling", diff --git a/veilid-core/src/network_manager.rs b/veilid-core/src/network_manager.rs index 233d4732..fe1dae4b 100644 --- a/veilid-core/src/network_manager.rs +++ b/veilid-core/src/network_manager.rs @@ -170,10 +170,12 @@ impl NetworkManager { Ok(()) } pub async fn terminate(&self) { - let mut inner = self.inner.lock(); - if let Some(routing_table) = &inner.routing_table { + let routing_table = { + let mut inner = self.inner.lock(); + inner.routing_table.take() + }; + if let Some(routing_table) = routing_table { routing_table.terminate().await; - inner.routing_table = None; } } diff --git a/veilid-core/src/tests/common/test_crypto.rs b/veilid-core/src/tests/common/test_crypto.rs index 5a370243..689e599f 100644 --- a/veilid-core/src/tests/common/test_crypto.rs +++ b/veilid-core/src/tests/common/test_crypto.rs @@ -19,10 +19,9 @@ fn setup_veilid_core() -> VeilidCoreSetup { } } -async fn startup(core: VeilidCore) -> VeilidAPI { +async fn startup() -> VeilidAPI { trace!("test_table_store: starting"); - let api = core - .startup(setup_veilid_core()) + let api = api_startup(setup_veilid_core()) .await .expect("startup failed"); api @@ -130,9 +129,8 @@ pub async fn test_dh(crypto: Crypto) { } pub async fn test_all() { - let core = VeilidCore::new(); - let api = startup(core.clone()).await; - let crypto = core.crypto(); + let api = startup().await; + let crypto = api.crypto().unwrap(); test_enc_dec().await; test_dh(crypto).await; shutdown(api.clone()).await; diff --git a/veilid-core/src/tests/common/test_envelope_receipt.rs b/veilid-core/src/tests/common/test_envelope_receipt.rs index c99a36e3..f353b527 100644 --- a/veilid-core/src/tests/common/test_envelope_receipt.rs +++ b/veilid-core/src/tests/common/test_envelope_receipt.rs @@ -8,13 +8,12 @@ use crate::*; pub async fn test_envelope_round_trip() { info!("--- test envelope round trip ---"); - let veilid_core = VeilidCore::new(); - let api = veilid_core - .startup(setup_veilid_core()) + let api = api_startup(setup_veilid_core()) .await .expect("startup failed"); + // Get crypto - let crypto = veilid_core.crypto(); + let crypto = api.crypto().unwrap(); // Create envelope let ts = 0x12345678ABCDEF69u64; diff --git a/veilid-core/src/tests/common/test_protected_store.rs b/veilid-core/src/tests/common/test_protected_store.rs index 58b2af95..269756dc 100644 --- a/veilid-core/src/tests/common/test_protected_store.rs +++ b/veilid-core/src/tests/common/test_protected_store.rs @@ -16,9 +16,9 @@ fn setup_veilid_core() -> VeilidCoreSetup { } } -async fn startup(core: VeilidCore) -> VeilidAPI { +async fn startup() -> VeilidAPI { trace!("test_table_store: starting"); - core.startup(setup_veilid_core()) + api_startup(setup_veilid_core()) .await .expect("startup failed") } @@ -93,10 +93,8 @@ pub async fn test_protected_store(ps: ProtectedStore) { } pub async fn test_all() { - let core = VeilidCore::new(); - let api = startup(core.clone()).await; - - let ps = core.protected_store(); + let api = startup().await; + let ps = api.protected_store().unwrap(); test_protected_store(ps.clone()).await; shutdown(api).await; diff --git a/veilid-core/src/tests/common/test_table_store.rs b/veilid-core/src/tests/common/test_table_store.rs index e19e0d47..44975ec3 100644 --- a/veilid-core/src/tests/common/test_table_store.rs +++ b/veilid-core/src/tests/common/test_table_store.rs @@ -17,9 +17,9 @@ fn setup_veilid_core() -> VeilidCoreSetup { } } -async fn startup(core: VeilidCore) -> VeilidAPI { +async fn startup() -> VeilidAPI { trace!("test_table_store: starting"); - core.startup(setup_veilid_core()) + api_startup(setup_veilid_core()) .await .expect("startup failed") } @@ -169,10 +169,8 @@ pub async fn test_cbor(ts: TableStore) { } pub async fn test_all() { - let core = VeilidCore::new(); - let api = startup(core.clone()).await; - - let ts = core.table_store(); + let api = startup().await; + let ts = api.table_store().unwrap(); test_delete_open_delete(ts.clone()).await; test_store_delete_load(ts.clone()).await; test_cbor(ts.clone()).await; diff --git a/veilid-core/src/tests/common/test_veilid_core.rs b/veilid-core/src/tests/common/test_veilid_core.rs index c0828248..889469c5 100644 --- a/veilid-core/src/tests/common/test_veilid_core.rs +++ b/veilid-core/src/tests/common/test_veilid_core.rs @@ -4,9 +4,7 @@ use crate::*; pub async fn test_startup_shutdown() { trace!("test_startup_shutdown: starting"); - let veilid_core = VeilidCore::new(); - let api = veilid_core - .startup(setup_veilid_core()) + let api = api_startup(setup_veilid_core()) .await .expect("startup failed"); trace!("test_startup_shutdown: shutting down"); @@ -15,11 +13,8 @@ pub async fn test_startup_shutdown() { } pub async fn test_attach_detach() { - let veilid_core = VeilidCore::new(); - info!("--- test normal order ---"); - let api = veilid_core - .startup(setup_veilid_core()) + let api = api_startup(setup_veilid_core()) .await .expect("startup failed"); api.attach().await.unwrap(); @@ -31,8 +26,7 @@ pub async fn test_attach_detach() { api.shutdown().await; info!("--- test auto detach ---"); - let api = veilid_core - .startup(setup_veilid_core()) + let api = api_startup(setup_veilid_core()) .await .expect("startup failed"); api.attach().await.unwrap(); @@ -40,8 +34,7 @@ pub async fn test_attach_detach() { api.shutdown().await; info!("--- test detach without attach ---"); - let api = veilid_core - .startup(setup_veilid_core()) + let api = api_startup(setup_veilid_core()) .await .expect("startup failed"); api.detach().await.unwrap(); diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 4d2c8f95..01f90fee 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -2,6 +2,7 @@ // Debugging use super::*; +use routing_table::*; fn get_bucket_entry_state(text: &str) -> Option { if text == "dead" { diff --git a/veilid-core/src/veilid_api/mod.rs b/veilid-core/src/veilid_api/mod.rs index cc515047..f43ce278 100644 --- a/veilid-core/src/veilid_api/mod.rs +++ b/veilid-core/src/veilid_api/mod.rs @@ -3,28 +3,38 @@ mod debug; pub use debug::*; -pub use crate::rpc_processor::InfoAnswer; use crate::*; -use api_logger::*; -use attachment_manager::*; -use core::fmt; -use network_manager::NetworkManager; -use routing_table::*; -use rpc_processor::{RPCError, RPCProcessor}; -use xx::*; -pub use crate::dht::key::{generate_secret, DHTKey, DHTKeySecret}; pub use crate::xx::{ IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, SystemPinBoxFuture, ToSocketAddrs, }; pub use alloc::string::ToString; +pub use attachment_manager::AttachmentManager; pub use core::str::FromStr; +pub use dht::crypto::Crypto; +pub use dht::key::{generate_secret, DHTKey, DHTKeySecret}; +pub use intf::BlockStore; +pub use intf::ProtectedStore; +pub use intf::TableStore; +pub use network_manager::NetworkManager; +pub use routing_table::RoutingTable; +pub use rpc_processor::InfoAnswer; + +use api_logger::*; +use core::fmt; +use core_context::{api_shutdown, VeilidCoreContext}; +use rpc_processor::{RPCError, RPCProcessor}; +use serde::*; +use xx::*; ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] +#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[serde(tag = "kind")] pub enum VeilidAPIError { + NotInitialized, + AlreadyInitialized, Timeout, Shutdown, NodeNotFound(NodeId), @@ -49,6 +59,8 @@ pub enum VeilidAPIError { impl fmt::Display for VeilidAPIError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { + VeilidAPIError::NotInitialized => write!(f, "VeilidAPIError::NotInitialized"), + VeilidAPIError::AlreadyInitialized => write!(f, "VeilidAPIError::AlreadyInitialized"), VeilidAPIError::Timeout => write!(f, "VeilidAPIError::Timeout"), VeilidAPIError::Shutdown => write!(f, "VeilidAPIError::Shutdown"), VeilidAPIError::NodeNotFound(ni) => write!(f, "VeilidAPIError::NodeNotFound({})", ni), @@ -107,7 +119,7 @@ macro_rules! parse_error { ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum VeilidLogLevel { Error = 1, Warn, @@ -128,7 +140,8 @@ impl VeilidLogLevel { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "kind")] pub enum VeilidUpdate { Log { log_level: VeilidLogLevel, @@ -137,14 +150,14 @@ pub enum VeilidUpdate { Attachment(AttachmentState), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct VeilidState { pub attachment: AttachmentState, } ///////////////////////////////////////////////////////////////////////////////////////////////////// /// -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord)] +#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] pub struct NodeId { pub key: DHTKey, } @@ -160,7 +173,7 @@ impl fmt::Display for NodeId { } } -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord)] +#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] pub struct ValueKey { pub key: DHTKey, pub subkey: Option, @@ -181,7 +194,7 @@ impl ValueKey { } } -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord)] +#[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] pub struct BlockId { pub key: DHTKey, } @@ -193,12 +206,12 @@ impl BlockId { ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Default)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Default, Serialize, Deserialize)] pub struct SenderInfo { pub socket_address: Option, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct NodeInfo { pub can_route: bool, pub will_route: bool, @@ -212,7 +225,7 @@ pub struct NodeInfo { pub will_validate_dial_info: bool, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)] // The derived ordering here is the order of preference, lower is preferred for connections // Must match DialInfo order pub enum ProtocolType { @@ -222,13 +235,13 @@ pub enum ProtocolType { WSS, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)] pub enum AddressType { IPV4, IPV6, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)] pub enum Address { IPV4(Ipv4Addr), IPV6(Ipv6Addr), @@ -310,7 +323,9 @@ impl FromStr for Address { } } -#[derive(Copy, Default, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive( + Copy, Default, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize, +)] pub struct SocketAddress { address: Address, port: u16, @@ -366,7 +381,7 @@ impl FromStr for SocketAddress { ////////////////////////////////////////////////////////////////// -#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct DialInfoFilter { pub peer_scope: PeerScope, pub protocol_type: Option, @@ -435,29 +450,30 @@ pub trait MatchesDialInfoFilter { fn matches_filter(&self, filter: &DialInfoFilter) -> bool; } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)] pub struct DialInfoUDP { pub socket_address: SocketAddress, } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)] pub struct DialInfoTCP { pub socket_address: SocketAddress, } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)] pub struct DialInfoWS { pub socket_address: SocketAddress, pub request: String, } -#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)] pub struct DialInfoWSS { pub socket_address: SocketAddress, pub request: String, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)] +#[serde(tag = "kind")] // The derived ordering here is the order of preference, lower is preferred for connections // Must match ProtocolType order pub enum DialInfo { @@ -706,7 +722,7 @@ impl MatchesDialInfoFilter for DialInfo { ////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub enum PeerScope { All, Global, @@ -718,13 +734,13 @@ impl Default for PeerScope { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct PeerInfo { pub node_id: NodeId, pub dial_infos: Vec, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub struct PeerAddress { pub socket_address: SocketAddress, pub protocol_type: ProtocolType, @@ -747,7 +763,7 @@ impl PeerAddress { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct ConnectionDescriptor { pub remote: PeerAddress, pub local: Option, @@ -802,7 +818,7 @@ impl MatchesDialInfoFilter for ConnectionDescriptor { ////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct NodeDialInfo { pub node_id: NodeId, pub dial_info: DialInfo, @@ -837,20 +853,20 @@ impl FromStr for NodeDialInfo { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct LatencyStats { pub fastest: u64, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies pub average: u64, // average latency over the ROLLING_LATENCIES_SIZE last latencies pub slowest: u64, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct TransferStatsDownUp { pub down: TransferStats, pub up: TransferStats, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct TransferStats { pub total: u64, // total amount transferred ever pub maximum: u64, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts @@ -858,7 +874,7 @@ pub struct TransferStats { pub minimum: u64, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct PingStats { pub in_flight: u32, // number of pings issued that have yet to be answered pub total_sent: u32, // number of pings that have been sent in the total_time range @@ -869,7 +885,7 @@ pub struct PingStats { pub recent_lost_pings: u32, // number of pings that have been lost since we lost reliability } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct PeerStats { pub time_added: u64, // when the peer was added to the routing table pub last_seen: Option, // when the peer was last seen for any reason, including when we first attempted to reach out to it @@ -889,7 +905,7 @@ cfg_if! { } } -#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] +#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize)] pub enum TunnelMode { Raw, Turn, @@ -897,7 +913,7 @@ pub enum TunnelMode { type TunnelId = u64; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct TunnelEndpoint { pub node_id: NodeId, // the node id of the tunnel endpoint pub dial_info: Vec, // multiple ways of how to get to the node @@ -914,7 +930,7 @@ impl Default for TunnelEndpoint { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct FullTunnel { pub id: TunnelId, pub timeout: u64, @@ -922,7 +938,7 @@ pub struct FullTunnel { pub remote: TunnelEndpoint, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct PartialTunnel { pub id: TunnelId, pub timeout: u64, @@ -931,12 +947,12 @@ pub struct PartialTunnel { ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct RouteHopSpec { pub dial_info: NodeDialInfo, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct PrivateRouteSpec { // pub public_key: DHTKey, @@ -944,7 +960,7 @@ pub struct PrivateRouteSpec { pub hops: Vec, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct SafetyRouteSpec { pub public_key: DHTKey, pub secret_key: DHTKeySecret, @@ -962,7 +978,7 @@ impl SafetyRouteSpec { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct RoutingContextOptions { pub safety_route_spec: Option, pub private_route_spec: Option, @@ -970,7 +986,7 @@ pub struct RoutingContextOptions { ///////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct SearchDHTAnswer { pub node_id: NodeId, pub dial_info: Vec, @@ -1051,26 +1067,19 @@ impl RoutingContext { ///////////////////////////////////////////////////////////////////////////////////////////////////// struct VeilidAPIInner { - core: Option, + context: Option, } impl fmt::Debug for VeilidAPIInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "VeilidAPIInner: {}", - match self.core { - Some(_) => "active", - None => "shutdown", - } - ) + write!(f, "VeilidAPIInner") } } impl Drop for VeilidAPIInner { fn drop(&mut self) { - if let Some(core) = self.core.take() { - intf::spawn_local(core.shutdown()).detach(); + if let Some(context) = self.context.take() { + intf::spawn_local(api_shutdown(context)).detach(); } } } @@ -1080,59 +1089,83 @@ pub struct VeilidAPI { inner: Arc>, } -#[derive(Clone, Debug, Default)] -pub struct VeilidAPIWeak { - inner: Weak>, -} - -impl VeilidAPIWeak { - pub fn upgrade(&self) -> Option { - self.inner.upgrade().map(|v| VeilidAPI { inner: v }) - } -} - impl VeilidAPI { - pub(crate) fn new(core: VeilidCore) -> Self { + pub(crate) fn new(context: VeilidCoreContext) -> Self { Self { - inner: Arc::new(Mutex::new(VeilidAPIInner { core: Some(core) })), + inner: Arc::new(Mutex::new(VeilidAPIInner { + context: Some(context), + })), } } - pub fn weak(&self) -> VeilidAPIWeak { - VeilidAPIWeak { - inner: Arc::downgrade(&self.inner), - } - } - fn core(&self) -> Result { - Ok(self - .inner - .lock() - .core - .as_ref() - .ok_or(VeilidAPIError::Shutdown)? - .clone()) - } - fn config(&self) -> Result { - Ok(self.core()?.config()) - } - fn attachment_manager(&self) -> Result { - Ok(self.core()?.attachment_manager()) - } - fn network_manager(&self) -> Result { - Ok(self.attachment_manager()?.network_manager()) - } - fn rpc_processor(&self) -> Result { - Ok(self.network_manager()?.rpc_processor()) - } pub async fn shutdown(self) { - let core = { self.inner.lock().core.take() }; - if let Some(core) = core { - core.shutdown().await; + let context = { self.inner.lock().context.take() }; + if let Some(context) = context { + api_shutdown(context).await; } } pub fn is_shutdown(&self) -> bool { - self.inner.lock().core.is_none() + self.inner.lock().context.is_none() + } + + //////////////////////////////////////////////////////////////// + // Accessors + pub fn config(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.config.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn crypto(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.crypto.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn table_store(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.table_store.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn block_store(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.block_store.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn protected_store(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.protected_store.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn attachment_manager(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.clone()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn network_manager(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.network_manager()); + } + Err(VeilidAPIError::NotInitialized) + } + pub fn rpc_processor(&self) -> Result { + let inner = self.inner.lock(); + if let Some(context) = &inner.context { + return Ok(context.attachment_manager.network_manager().rpc_processor()); + } + Err(VeilidAPIError::NotInitialized) } //////////////////////////////////////////////////////////////// diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 97a1edee..f46ed0a4 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -2,6 +2,8 @@ use crate::dht::key; use crate::intf; use crate::xx::*; +use serde::*; + cfg_if! { if #[cfg(target_arch = "wasm32")] { pub type ConfigCallbackReturn = Result, String>; @@ -9,7 +11,7 @@ cfg_if! { } else { pub type ConfigCallbackReturn = Result, String>; - pub type ConfigCallback = Arc ConfigCallbackReturn + Send>; + pub type ConfigCallback = Arc ConfigCallbackReturn + Send + Sync>; } } @@ -172,7 +174,7 @@ pub struct VeilidConfigCapabilities { pub protocol_accept_wss: bool, } -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)] pub enum VeilidConfigLogLevel { Off, Error, diff --git a/veilid-core/src/veilid_core.rs b/veilid-core/src/veilid_core.rs deleted file mode 100644 index 9486fbe3..00000000 --- a/veilid-core/src/veilid_core.rs +++ /dev/null @@ -1,241 +0,0 @@ -use crate::api_logger::*; -use crate::attachment_manager::*; -use crate::dht::crypto::Crypto; -use crate::intf::*; -use crate::veilid_api::*; -use crate::veilid_config::*; -use crate::xx::*; - -cfg_if! { - if #[cfg(target_arch = "wasm32")] { - pub type UpdateCallback = Arc SystemPinBoxFuture<()>>; - } else { - pub type UpdateCallback = Arc SystemPinBoxFuture<()> + Send + Sync>; - } -} - -pub struct VeilidCoreSetup { - pub update_callback: UpdateCallback, - pub config_callback: ConfigCallback, -} - -struct VeilidCoreInner { - config: Option, - protected_store: Option, - table_store: Option, - crypto: Option, - attachment_manager: Option, - api: VeilidAPIWeak, -} - -#[derive(Clone)] -pub struct VeilidCore { - inner: Arc>, -} - -impl Default for VeilidCore { - fn default() -> Self { - Self::new() - } -} - -impl VeilidCore { - fn new_inner() -> VeilidCoreInner { - VeilidCoreInner { - config: None, - table_store: None, - protected_store: None, - crypto: None, - attachment_manager: None, - api: VeilidAPIWeak::default(), - } - } - pub fn new() -> Self { - Self { - inner: Arc::new(Mutex::new(Self::new_inner())), - } - } - - pub(crate) fn config(&self) -> VeilidConfig { - self.inner.lock().config.as_ref().unwrap().clone() - } - - pub(crate) fn table_store(&self) -> TableStore { - self.inner.lock().table_store.as_ref().unwrap().clone() - } - - pub(crate) fn protected_store(&self) -> ProtectedStore { - self.inner.lock().protected_store.as_ref().unwrap().clone() - } - - pub(crate) fn crypto(&self) -> Crypto { - self.inner.lock().crypto.as_ref().unwrap().clone() - } - - pub(crate) fn attachment_manager(&self) -> AttachmentManager { - self.inner - .lock() - .attachment_manager - .as_ref() - .unwrap() - .clone() - } - - // internal startup - async fn internal_startup( - &self, - inner: &mut VeilidCoreInner, - setup: VeilidCoreSetup, - ) -> Result { - // Start up api logging early if it's in the config - let api_log_level: VeilidConfigLogLevel = - *(setup.config_callback)("api_log_level".to_owned())? - .downcast() - .map_err(|_| "incorrect type for key 'api_log_level'".to_owned())?; - if api_log_level != VeilidConfigLogLevel::Off { - ApiLogger::init( - api_log_level.to_level_filter(), - setup.update_callback.clone(), - ); - for ig in crate::DEFAULT_LOG_IGNORE_LIST { - ApiLogger::add_filter_ignore_str(ig); - } - } - - trace!("VeilidCore::internal_startup starting"); - - cfg_if! { - if #[cfg(target_os = "android")] { - if utils::android::ANDROID_GLOBALS.lock().is_none() { - error!("Android globals are not set up"); - return Err("Android globals are not set up".to_owned()); - } - } - } - - // Set up config - trace!("VeilidCore::internal_startup init config"); - let mut config = VeilidConfig::new(); - config.init(setup.config_callback).await?; - inner.config = Some(config.clone()); - - // Set up protected store - trace!("VeilidCore::internal_startup init protected store"); - let protected_store = ProtectedStore::new(config.clone()); - protected_store.init().await?; - inner.protected_store = Some(protected_store.clone()); - - // Init node id from config now that protected store is set up - config.init_node_id(protected_store).await?; - - // Set up tablestore - trace!("VeilidCore::internal_startup init table store"); - let table_store = TableStore::new(config.clone()); - table_store.init().await?; - inner.table_store = Some(table_store.clone()); - - // Set up crypto - trace!("VeilidCore::internal_startup init crypto"); - let crypto = Crypto::new(config.clone(), table_store.clone()); - crypto.init().await?; - inner.crypto = Some(crypto.clone()); - - // Set up block store - // trace!("VeilidCore::internal_startup init block store"); - // let block_store = BlockStore::new(config.clone()); - // block_store.init().await?; - // inner.block_store = Some(block_store.clone();) - - // Set up attachment manager - trace!("VeilidCore::internal_startup init attachment manager"); - let cb = setup.update_callback; - let attachment_manager = - AttachmentManager::new(config.clone(), table_store.clone(), crypto.clone()); - attachment_manager - .init(Arc::new( - move |_old_state: AttachmentState, new_state: AttachmentState| { - cb(VeilidUpdate::Attachment(new_state)) - }, - )) - .await?; - inner.attachment_manager = Some(attachment_manager.clone()); - - // Set up the API - trace!("VeilidCore::internal_startup init API"); - let this = self.clone(); - let veilid_api = VeilidAPI::new(this); - inner.api = veilid_api.weak(); - - trace!("VeilidCore::internal_startup complete"); - - Ok(veilid_api) - } - - // called once at the beginning to start the node - pub async fn startup(&self, setup: VeilidCoreSetup) -> Result { - // See if we have an API started up already - let mut inner = self.inner.lock(); - if inner.api.upgrade().is_some() { - // If so, return an error because we shouldn't try to do this more than once - return Err("Veilid API is started".to_owned()); - } - - // Ensure we never end up partially initialized - match self.internal_startup(&mut *inner, setup).await { - Ok(v) => Ok(v), - Err(e) => { - Self::internal_shutdown(&mut *inner).await; - Err(e) - } - } - } - - async fn internal_shutdown(inner: &mut VeilidCoreInner) { - trace!("VeilidCore::internal_shutdown starting"); - - // Detach the API object - inner.api = VeilidAPIWeak::default(); - - // Shut down up attachment manager - if let Some(attachment_manager) = &inner.attachment_manager { - attachment_manager.terminate().await; - inner.attachment_manager = None; - } - - // Shut down crypto - if let Some(crypto) = &inner.crypto { - crypto.terminate().await; - inner.crypto = None; - } - - // Shut down table store - if let Some(table_store) = &inner.table_store { - table_store.terminate().await; - inner.table_store = None; - } - - // Shut down protected store - if let Some(protected_store) = &inner.protected_store { - protected_store.terminate().await; - inner.protected_store = None; - } - - // Shut down config - if let Some(config) = &inner.config { - config.terminate().await; - inner.config = None; - } - - trace!("VeilidCore::shutdown complete"); - - ApiLogger::terminate(); - } - - // stop the node gracefully because the veilid api was dropped - pub(crate) async fn shutdown(self) { - let mut inner = self.inner.lock(); - Self::internal_shutdown(&mut *inner).await; - } - - // -} diff --git a/veilid-core/src/xx/log_thru.rs b/veilid-core/src/xx/log_thru.rs index ee2ba8a9..2aa1e2d1 100644 --- a/veilid-core/src/xx/log_thru.rs +++ b/veilid-core/src/xx/log_thru.rs @@ -158,6 +158,18 @@ macro_rules! logthru_pstore { logthru!($($level)? "pstore", $fmt, $($arg),+) } } +#[macro_export] +macro_rules! logthru_crypto { + ($($level:ident)?) => { + logthru!($($level)? "crypto") + }; + ($($level:ident)? $text:literal) => { + logthru!($($level)? "crypto", $text) + }; + ($($level:ident)? $fmt:literal, $($arg:expr),+) => { + logthru!($($level)? "crypto", $fmt, $($arg),+) + } +} #[macro_export] macro_rules! logthru { diff --git a/veilid-flutter/android/src/main/kotlin/com/veilid/veilid/VeilidPlugin.kt b/veilid-flutter/android/src/main/kotlin/com/veilid/veilid/VeilidPlugin.kt index c54145b8..87455977 100644 --- a/veilid-flutter/android/src/main/kotlin/com/veilid/veilid/VeilidPlugin.kt +++ b/veilid-flutter/android/src/main/kotlin/com/veilid/veilid/VeilidPlugin.kt @@ -1,7 +1,7 @@ package com.veilid.veilid import androidx.annotation.NonNull - +import android.content.Context import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @@ -10,32 +10,23 @@ import io.flutter.plugin.common.MethodChannel.Result /** VeilidPlugin */ class VeilidPlugin: FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - // private lateinit var channel : MethodChannel + + class object { + { + System.loadLibrary("veilid_flutter"); + } + } - // static { - // System.loadLibrary("veilid_flutter"); - // } -// xxx get main activity - // private static native void init_android(Context context); + native fun init_android(ctx: Context) override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - // channel = MethodChannel(flutterPluginBinding.binaryMessenger, "veilid") - // channel.setMethodCallHandler(this) + init_android(flutterPluginBinding.getApplicationContext()) } override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - // if (call.method == "getPlatformVersion") { - // result.success("Android ${android.os.Build.VERSION.RELEASE}") - // } else { result.notImplemented() - // } } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - // channel.setMethodCallHandler(null) } } diff --git a/veilid-flutter/example/pubspec.lock b/veilid-flutter/example/pubspec.lock index fe689f91..76cf02c8 100644 --- a/veilid-flutter/example/pubspec.lock +++ b/veilid-flutter/example/pubspec.lock @@ -50,6 +50,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" fake_async: dependency: transitive description: @@ -121,6 +128,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -128,6 +142,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + oxidized: + dependency: transitive + description: + name: oxidized + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" path: dependency: transitive description: @@ -181,7 +202,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.8" typed_data: dependency: transitive description: diff --git a/veilid-flutter/lib/bridge_generated.freezed.dart b/veilid-flutter/lib/bridge_generated.freezed.dart deleted file mode 100644 index 2e654245..00000000 --- a/veilid-flutter/lib/bridge_generated.freezed.dart +++ /dev/null @@ -1,367 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target - -part of 'bridge_generated.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -class _$VeilidUpdateTearOff { - const _$VeilidUpdateTearOff(); - - Log log({required VeilidLogLevel logLevel, required String message}) { - return Log( - logLevel: logLevel, - message: message, - ); - } - - Attachment attachment(AttachmentState field0) { - return Attachment( - field0, - ); - } -} - -/// @nodoc -const $VeilidUpdate = _$VeilidUpdateTearOff(); - -/// @nodoc -mixin _$VeilidUpdate { - @optionalTypeArgs - TResult when({ - required TResult Function(VeilidLogLevel logLevel, String message) log, - required TResult Function(AttachmentState field0) attachment, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult Function(VeilidLogLevel logLevel, String message)? log, - TResult Function(AttachmentState field0)? attachment, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(VeilidLogLevel logLevel, String message)? log, - TResult Function(AttachmentState field0)? attachment, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(Log value) log, - required TResult Function(Attachment value) attachment, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult Function(Log value)? log, - TResult Function(Attachment value)? attachment, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Log value)? log, - TResult Function(Attachment value)? attachment, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $VeilidUpdateCopyWith<$Res> { - factory $VeilidUpdateCopyWith( - VeilidUpdate value, $Res Function(VeilidUpdate) then) = - _$VeilidUpdateCopyWithImpl<$Res>; -} - -/// @nodoc -class _$VeilidUpdateCopyWithImpl<$Res> implements $VeilidUpdateCopyWith<$Res> { - _$VeilidUpdateCopyWithImpl(this._value, this._then); - - final VeilidUpdate _value; - // ignore: unused_field - final $Res Function(VeilidUpdate) _then; -} - -/// @nodoc -abstract class $LogCopyWith<$Res> { - factory $LogCopyWith(Log value, $Res Function(Log) then) = - _$LogCopyWithImpl<$Res>; - $Res call({VeilidLogLevel logLevel, String message}); -} - -/// @nodoc -class _$LogCopyWithImpl<$Res> extends _$VeilidUpdateCopyWithImpl<$Res> - implements $LogCopyWith<$Res> { - _$LogCopyWithImpl(Log _value, $Res Function(Log) _then) - : super(_value, (v) => _then(v as Log)); - - @override - Log get _value => super._value as Log; - - @override - $Res call({ - Object? logLevel = freezed, - Object? message = freezed, - }) { - return _then(Log( - logLevel: logLevel == freezed - ? _value.logLevel - : logLevel // ignore: cast_nullable_to_non_nullable - as VeilidLogLevel, - message: message == freezed - ? _value.message - : message // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc - -class _$Log implements Log { - const _$Log({required this.logLevel, required this.message}); - - @override - final VeilidLogLevel logLevel; - @override - final String message; - - @override - String toString() { - return 'VeilidUpdate.log(logLevel: $logLevel, message: $message)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is Log && - const DeepCollectionEquality().equals(other.logLevel, logLevel) && - const DeepCollectionEquality().equals(other.message, message)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(logLevel), - const DeepCollectionEquality().hash(message)); - - @JsonKey(ignore: true) - @override - $LogCopyWith get copyWith => _$LogCopyWithImpl(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(VeilidLogLevel logLevel, String message) log, - required TResult Function(AttachmentState field0) attachment, - }) { - return log(logLevel, message); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult Function(VeilidLogLevel logLevel, String message)? log, - TResult Function(AttachmentState field0)? attachment, - }) { - return log?.call(logLevel, message); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(VeilidLogLevel logLevel, String message)? log, - TResult Function(AttachmentState field0)? attachment, - required TResult orElse(), - }) { - if (log != null) { - return log(logLevel, message); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Log value) log, - required TResult Function(Attachment value) attachment, - }) { - return log(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult Function(Log value)? log, - TResult Function(Attachment value)? attachment, - }) { - return log?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Log value)? log, - TResult Function(Attachment value)? attachment, - required TResult orElse(), - }) { - if (log != null) { - return log(this); - } - return orElse(); - } -} - -abstract class Log implements VeilidUpdate { - const factory Log( - {required VeilidLogLevel logLevel, required String message}) = _$Log; - - VeilidLogLevel get logLevel; - String get message; - @JsonKey(ignore: true) - $LogCopyWith get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $AttachmentCopyWith<$Res> { - factory $AttachmentCopyWith( - Attachment value, $Res Function(Attachment) then) = - _$AttachmentCopyWithImpl<$Res>; - $Res call({AttachmentState field0}); -} - -/// @nodoc -class _$AttachmentCopyWithImpl<$Res> extends _$VeilidUpdateCopyWithImpl<$Res> - implements $AttachmentCopyWith<$Res> { - _$AttachmentCopyWithImpl(Attachment _value, $Res Function(Attachment) _then) - : super(_value, (v) => _then(v as Attachment)); - - @override - Attachment get _value => super._value as Attachment; - - @override - $Res call({ - Object? field0 = freezed, - }) { - return _then(Attachment( - field0 == freezed - ? _value.field0 - : field0 // ignore: cast_nullable_to_non_nullable - as AttachmentState, - )); - } -} - -/// @nodoc - -class _$Attachment implements Attachment { - const _$Attachment(this.field0); - - @override - final AttachmentState field0; - - @override - String toString() { - return 'VeilidUpdate.attachment(field0: $field0)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is Attachment && - const DeepCollectionEquality().equals(other.field0, field0)); - } - - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(field0)); - - @JsonKey(ignore: true) - @override - $AttachmentCopyWith get copyWith => - _$AttachmentCopyWithImpl(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(VeilidLogLevel logLevel, String message) log, - required TResult Function(AttachmentState field0) attachment, - }) { - return attachment(field0); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult Function(VeilidLogLevel logLevel, String message)? log, - TResult Function(AttachmentState field0)? attachment, - }) { - return attachment?.call(field0); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(VeilidLogLevel logLevel, String message)? log, - TResult Function(AttachmentState field0)? attachment, - required TResult orElse(), - }) { - if (attachment != null) { - return attachment(field0); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Log value) log, - required TResult Function(Attachment value) attachment, - }) { - return attachment(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult Function(Log value)? log, - TResult Function(Attachment value)? attachment, - }) { - return attachment?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Log value)? log, - TResult Function(Attachment value)? attachment, - required TResult orElse(), - }) { - if (attachment != null) { - return attachment(this); - } - return orElse(); - } -} - -abstract class Attachment implements VeilidUpdate { - const factory Attachment(AttachmentState field0) = _$Attachment; - - AttachmentState get field0; - @JsonKey(ignore: true) - $AttachmentCopyWith get copyWith => - throw _privateConstructorUsedError; -} diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 4ce0b245..9afcebe1 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -1,31 +1,92 @@ import 'dart:async'; -import 'dart:ffi'; -import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; -import 'package:veilid/bridge_generated.dart'; +import 'package:oxidized/oxidized.dart'; -const base = 'veilid_flutter'; -final path = Platform.isWindows - ? '$base.dll' - : Platform.isMacOS - ? 'lib$base.dylib' - : 'lib$base.so'; -late final dylib = Platform.isIOS ? DynamicLibrary.process() : DynamicLibrary.open(path); -late final veilidApi = VeilidFlutterImpl(dylib); +import 'veilid_stub.dart' + if (dart.library.io) 'veilid_ffi.dart' + if (dart.library.js) 'veilid_js.dart'; -class Veilid { +////////////////////////////////////////////////////////// - static VeilidFlutterImpl get api { - if (veilidApi == null) { - throw PlatformException( - code: 'Library missing', - details: 'veilid_flutter library could not be loaded dynamically', - ); - } - return veilidApi; +enum AttachmentState { + Detached, + Attaching, + AttachedWeak, + AttachedGood, + AttachedStrong, + FullyAttached, + OverAttached, + Detaching, +} + +enum VeilidLogLevel { + Error, + Warn, + Info, + Debug, + Trace, +} + +// VeilidVersion + +class VeilidVersion { + final int major; + final int minor; + final int patch; + + VeilidVersion({ + required this.major, + required this.minor, + required this.patch, + }); +} + +// VeilidUpdate + +abstract class VeilidUpdate { + VeilidUpdateKind get kind; +} + +class VeilidUpdateLog implements VeilidUpdate { + final VeilidLogLevel logLevel; + final String message; + + VeilidUpdateLog(this.logLevel, this.message); +} + +class VeilidUpdateAttachment implements VeilidUpdate { + final AttachmentState state; + + VeilidUpdateAttachment(this.state); +} + +// VeilidState + +class VeilidState { + final AttachmentState attachment; + + VeilidState(this.attachment); +} + + + +// Veilid singleton factory + +abstract class Veilid { + static Veilid _instance; + + static Veilid get instance { + _instance ??= getVeilid(); + return _instance; } + Stream startupVeilidCore(String config); + Future> getVeilidState(); + Future> changeApiLogLevel(VeilidLogLevel logLevel); + Future> shutdownVeilidCore(); + String veilidVersionString(); + VeilidVersion veilidVersion(); } diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart new file mode 100644 index 00000000..37c11e61 --- /dev/null +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -0,0 +1,106 @@ +import 'dart:async'; +import 'dart:ffi' as ffi; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; +import 'package:oxidized/oxidized.dart'; + +////////////////////////////////////////////////////////// + +// Load the veilid_flutter library once +const _base = 'veilid_flutter'; +final _path = Platform.isWindows + ? '$_base.dll' + : Platform.isMacOS + ? 'lib$_base.dylib' + : 'lib$_base.so'; +late final _dylib = Platform.isIOS ? DynamicLibrary.process() : DynamicLibrary.open(_path); + +// Linkage for initialization +typedef _dart_postCObject = NativeFunction)>; +// fn free_string(s: *mut std::os::raw::c_char) +typedef _free_string_C = Void Function(Pointer); +typedef _free_string_Dart = void Function(Pointer); +// fn initialize_veilid_flutter(dart_post_c_object_ptr: ffi::DartPostCObjectFnType) +typedef _initializeVeilidFlutter_C = Void Function(Pointer<_dart_postCObject>); +typedef _initializeVeilidFlutter_Dart = void Function(Pointer<_dart_postCObject>); +// fn startup_veilid_core(port: i64, config: FfiStr) +typedef _startup_veilid_core_C = Void Function(Int64, Pointer); +typedef _startup_veilid_core_Dart = void Function(int, Pointer); +// fn get_veilid_state(port: i64) +typedef _get_veilid_state_C = Void Function(Int64); +typedef _get_veilid_state_Dart = void Function(int); +// fn change_api_log_level(port: i64, log_level: FfiStr) +typedef _change_api_log_level_C = Void Function(Int64, Pointer); +typedef _change_api_log_level_Dart = void Function(int, Pointer); +// fn shutdown_veilid_core(port: i64) +typedef _shutdown_veilid_core_C = Void Function(Int64); +typedef _shutdown_veilid_core_Dart = void Function(int); +// fn veilid_version_string() -> *mut c_char +typedef _veilid_version_string_C = Pointer Function(); +typedef _veilid_version_string_Dart = Pointer Function(); +// fn veilid_version() -> VeilidVersion +class VeilidVersion extends Struct { + @Uint32() + external int major; + @Uint32() + external int minor; + @Uint32() + external int patch; +} +typedef _veilid_version_C = VeilidVersion Function(); +typedef _veilid_version_Dart = VeilidVersion Function(); + +// Interface factory for high level Veilid API +Veilid getVeilid() => VeilidFFI(_dylib); + +// FFI implementation of high level Veilid API +class VeilidFFI { + // veilid_core shared library + final DynamicLibrary _dylib; + + // Shared library functions + final _free_string_Dart _freeString; + final _startup_veilid_core_Dart _startupVeilidCore; + final _get_veilid_state_Dart _getVeilidState; + final _change_api_log_level_Dart _changeApiLogLevel; + final _shutdown_veilid_core_Dart _shutdownVeilidCore; + final _veilid_version_string_Dart _veilidVersionString; + final _veilid_version_Dat _veilidVersion; + + VeilidFFI(DynamicLibrary dylib): _dylib = dylib { + var initializeVeilidFlutter = _dylib.lookupFunction<_initializeVeilidFlutter_C, _initializeVeilidFlutter_Dart>('initialize_veilid_flutter'); + initializeVeilidFlutter(NativeApi.postCObject); + + // Look up shared library functions + _freeString = dylib.lookupFunction<_free_string_C, _free_string_Dart>('free_string'); + _startupVeilidCore = dylib.lookupFunction<_startup_veilid_core_C, _startup_veilid_core_Dart>('startup_veilid_core'); + _getveilidState = dylib.lookupFunction<_get_veilid_state_C, _get_veilid_state_Dart>('get_veilid_state'); + _changeApiLogLevel = dylib.lookupFunction<_change_api_log_level_C, _change_api_log_level_Dart>('change_api_log_level'); + _shutdownVeilidCore = dylib.lookupFunction<_shutdown_veilid_core_C, _shutdown_veilid_core_Dart>('shutdown_veilid_core'); + _veilidVersionString = dylib.lookupFunction<_veilid_version_string_C, _veilid_version_string_Dart>('veilid_version_string'); + _veilidVersion = dylib.lookupFunction<_veilid_version_C, _veilid_version_Dart>('veilid_version'); + } + + Stream startupVeilidCore(String config); + Future> getVeilidState(); + Future> changeApiLogLevel(VeilidLogLevel logLevel); + Future> shutdownVeilidCore() async { + // xxx continue here + } + + String veilidVersionString() { + final version_string = _veilidVersionString(); + String ret = version_string.toDartString(); + _freeString(version_string); + return version_string; + } + + VeilidVersion veilidVersion() { + return _veilidVersion(); + } + +} diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart new file mode 100644 index 00000000..0b83aaeb --- /dev/null +++ b/veilid-flutter/lib/veilid_js.dart @@ -0,0 +1,41 @@ +import 'veilid.dart'; + +import 'dart:js'; +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; +import 'package:oxidized/oxidized.dart'; + +////////////////////////////////////////////////////////// + +Veilid getVeilid() => VeilidJS(); + +class VeilidJS { + + Stream startupVeilidCore(Object? configCallback(String key)) { + throw UnimplementedError(); + } + + Future getVeilidState() { + throw UnimplementedError(); + } + + Future changeApiLogLevel(VeilidLogLevel logLevel) { + throw UnimplementedError(); + } + + Future shutdownVeilidCore() { + throw UnimplementedError(); + } + + Future veilidVersionString() { + throw UnimplementedError(); + } + + Future veilidVersion() { + throw UnimplementedError(); + } + +} diff --git a/veilid-flutter/lib/veilid_web.dart b/veilid-flutter/lib/veilid_plugin_stub_web.dart similarity index 97% rename from veilid-flutter/lib/veilid_web.dart rename to veilid-flutter/lib/veilid_plugin_stub_web.dart index 50867996..bd2398c8 100644 --- a/veilid-flutter/lib/veilid_web.dart +++ b/veilid-flutter/lib/veilid_plugin_stub_web.dart @@ -11,7 +11,7 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart'; // xxx link in WASM version of veilid-flutter /// A web implementation of the Veilid plugin. -class VeilidWeb { +class VeilidPluginStubWeb { static void registerWith(Registrar registrar) { // final MethodChannel channel = MethodChannel( // 'veilid', diff --git a/veilid-flutter/lib/veilid_stub.dart b/veilid-flutter/lib/veilid_stub.dart new file mode 100644 index 00000000..1c56bed6 --- /dev/null +++ b/veilid-flutter/lib/veilid_stub.dart @@ -0,0 +1,3 @@ +import 'veilid.dart' + +Veilid getVeilid() => throw UnsupportedError('Cannot create Veilid object'); diff --git a/veilid-flutter/pubspec.yaml b/veilid-flutter/pubspec.yaml index 71326b2c..c4b747c7 100644 --- a/veilid-flutter/pubspec.yaml +++ b/veilid-flutter/pubspec.yaml @@ -15,6 +15,8 @@ dependencies: sdk: flutter flutter_rust_bridge: ^1.14.0 freezed_annotation: ^1.1.0 + oxidized: ^5.1.0 + ffi: ^1.1.2 dev_dependencies: flutter_test: @@ -46,8 +48,8 @@ flutter: windows: pluginClass: VeilidPlugin web: - pluginClass: VeilidWeb - fileName: veilid_web.dart + pluginClass: VeilidPluginStubWeb + fileName: veilid_plugin_stub_web.dart # To add assets to your plugin package, add an assets section, like this: # assets: diff --git a/veilid-flutter/rust/Cargo.toml b/veilid-flutter/rust/Cargo.toml index b6a8c948..66c15ee7 100644 --- a/veilid-flutter/rust/Cargo.toml +++ b/veilid-flutter/rust/Cargo.toml @@ -4,16 +4,39 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib", "staticlib"] +crate-type = ["cdylib", "staticlib", "rlib"] [dependencies] -async-std = { version = "^1", features = ["unstable"] } veilid-core = { path="../../veilid-core" } -flutter_rust_bridge = "^1" parking_lot = "^0" log = "^0" -anyhow = { version = "^1", features = ["backtrace"] } cfg-if = "^1" +backtrace = "^0" +serde_json = "^1" +serde = "^1" +futures = "^0" + +# Dependencies for native builds only +# Linux, Windows, Mac, iOS, Android +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +async-std = { version = "^1", features = ["unstable"] } +anyhow = { version = "^1", features = ["backtrace"] } +allo-isolate = "^0" +ffi-support = "^0" +lazy_static = "^1" + +# Dependencies for WASM builds only +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "^0" +console_error_panic_hook = "^0" +wee_alloc = "^0" +wasm-logger = "^0" +wasm-bindgen-futures = "^0" +js-sys = "^0" + +# Dev Dependencies for WASM builds only +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "^0" [build-dependencies] cfg-if = "^1" diff --git a/veilid-flutter/rust/build.rs b/veilid-flutter/rust/build.rs deleted file mode 100644 index 07c45f16..00000000 --- a/veilid-flutter/rust/build.rs +++ /dev/null @@ -1,155 +0,0 @@ -use cfg_if::*; -use std::env; -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; -use std::process::Command; - -fn resolve_llvm_path() -> Option { - cfg_if! { - if #[cfg(target_os="linux")] { - // build host is linux - let paths: Vec = - env::var_os("PATH").map(|paths| env::split_paths(&paths).collect())?; - - // find clang - let d = paths.iter().find_map(|p| { - if p.join("clang").exists() { - if let Ok(real_clang_path) = std::fs::canonicalize(p.join("clang")) { - if let Some(llvmbindir) = real_clang_path.parent() { - if let Some(llvmdir) = llvmbindir.parent() { - return Some(llvmdir.to_owned()); - } - } - } - } - None - }); - - d.or_else(|| { - ["/usr/lib/llvm-13", "/usr/lib/llvm-12", "/usr/lib/llvm-11", "/usr/lib/llvm-10"].iter().map(Path::new).find_map(|p| if p.exists() { Some(p.to_owned()) } else { None } ) - }) - - } else if #[cfg(target_os="macos")] { - // build host is mac - ["/usr/local/opt/llvm", "/opt/homebrew/opt/llvm", ].iter().map(Path::new).find_map(|p| if p.exists() { Some(p.to_owned()) } else { None } ) - } else { - // anywhere else, just use the default paths - None - } - } -} - -fn main() { - //let out_dir = env::var_os("OUT_DIR").unwrap(); - let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); - - let input_path = Path::new(&manifest_dir).join("src").join("api.rs"); - let output_path = Path::new(&manifest_dir) - .parent() - .unwrap() - .join("lib") - .join("bridge_generated.dart"); - let c_path = Path::new(&manifest_dir) - .parent() - .unwrap() - .join("ios") - .join("Classes") - .join("bridge_generated.h"); - let llvm_path = resolve_llvm_path(); - - //eprintln!("input_path: {:?}", input_path); - //eprintln!("output_path: {:?}", output_path); - //eprintln!("c_path: {:?}", c_path); - //eprintln!("llvm_path: {:?}", llvm_path); - - let mut command = Command::new("flutter_rust_bridge_codegen"); - if let Some(llvm_path) = llvm_path { - command.args([ - OsStr::new("--rust-input"), - input_path.as_os_str(), - OsStr::new("--dart-output"), - output_path.as_os_str(), - OsStr::new("--c-output"), - c_path.as_os_str(), - OsStr::new("--llvm-path"), - llvm_path.as_os_str(), - ]); - } else { - command.args([ - OsStr::new("--rust-input"), - input_path.as_os_str(), - OsStr::new("--dart-output"), - output_path.as_os_str(), - OsStr::new("--c-output"), - c_path.as_os_str(), - ]); - } - - let mut child = command - .spawn() - .expect("flutter_rust_bridge_codegen did not execute correctly"); - child - .wait() - .expect("flutter_rust_bridge_codegen was not running"); - - // Flutter pub get - // Run: flutter pub get - - let mut command; - cfg_if! { - if #[cfg(target_os="windows")] { - command = Command::new("cmd"); - command.args([ - OsStr::new("/c"), - OsStr::new("flutter"), - OsStr::new("pub"), - OsStr::new("get"), - ]); - } else { - command = Command::new("flutter"); - command.args([ - OsStr::new("pub"), - OsStr::new("get"), - ]); - } - } - - let mut child = command - .spawn() - .expect("'flutter pub get' did not execute correctly"); - child.wait().expect("'flutter pub get' was not running"); - - // Build freezed - // Run: flutter pub run build_runner build - - let mut command; - cfg_if! { - if #[cfg(target_os="windows")] { - command = Command::new("cmd"); - command.args([ - OsStr::new("/c"), - OsStr::new("flutter"), - OsStr::new("pub"), - OsStr::new("run"), - OsStr::new("build_runner"), - OsStr::new("build"), - ]); - } else { - command = Command::new("flutter"); - command.args([ - OsStr::new("pub"), - OsStr::new("run"), - OsStr::new("build_runner"), - OsStr::new("build"), - OsStr::new("--delete-conflicting-outputs"), - ]); - } - } - - let mut child = command - .spawn() - .expect("'flutter pub run build_runner build' did not execute correctly"); - child - .wait() - .expect("'flutter pub run build_runner build' was not running"); -} diff --git a/veilid-flutter/rust/src/api.rs b/veilid-flutter/rust/src/api.rs deleted file mode 100644 index 8013f9d3..00000000 --- a/veilid-flutter/rust/src/api.rs +++ /dev/null @@ -1,608 +0,0 @@ -use anyhow::*; -use async_std::sync::Mutex as AsyncMutex; -use cfg_if::*; -use flutter_rust_bridge::*; -use log::*; -use std::fmt; -use std::sync::Arc; - -// Globals - -static VEILID_API: AsyncMutex> = AsyncMutex::new(None); -async fn get_veilid_api() -> Result { - let api_lock = VEILID_API.lock().await; - let veilid_api = match &*api_lock { - None => { - return Err(anyhow!(VeilidAPIError::NotInitialized)); - } - Some(api) => api.clone(), - }; - Ok(veilid_api) -} -async fn take_veilid_api() -> Result { - let mut api_lock = VEILID_API.lock().await; - let veilid_api = match api_lock.take() { - None => { - return Err(anyhow!(VeilidAPIError::NotInitialized)); - } - Some(api) => api, - }; - Ok(veilid_api) -} - -///////////////////////////////////////// -// Config Settings -// Not all settings available through Veilid API are available to Flutter applications - -#[derive(Debug, Default, Clone)] -#[allow(non_snake_case)] -pub struct VeilidConfig { - pub program_name: String, - pub veilid_namespace: String, - pub api_log_level: VeilidLogLevel, - // Capabilities - pub capabilities__protocol_udp: bool, - pub capabilities__protocol_connect_tcp: bool, - pub capabilities__protocol_accept_tcp: bool, - pub capabilities__protocol_connect_ws: bool, - pub capabilities__protocol_accept_ws: bool, - pub capabilities__protocol_connect_wss: bool, - pub capabilities__protocol_accept_wss: bool, - // Protected Store - pub protected_store__allow_insecure_fallback: bool, - pub protected_store__always_use_insecure_storage: bool, - pub protected_store__insecure_fallback_directory: String, - pub protected_store__delete: bool, - // Table Store - pub table_store__directory: String, - pub table_store__delete: bool, - // Block Store - pub block_store__directory: String, - pub block_store__delete: bool, - // Network - pub network__max_connections: u32, - pub network__connection_initial_timeout_ms: u32, - pub network__node_id: String, - pub network__node_id_secret: String, - pub network__bootstrap: Vec, - pub network__upnp: bool, - pub network__natpmp: bool, - pub network__enable_local_peer_scope: bool, - pub network__restricted_nat_retries: u32, - // Network / RPC - pub network__rpc__concurrency: u32, - pub network__rpc__queue_size: u32, - pub network__rpc__max_timestamp_behind_ms: Option, - pub network__rpc__max_timestamp_ahead_ms: Option, - pub network__rpc__timeout_ms: u32, - pub network__rpc__max_route_hop_count: u8, - // Network / DHT - pub network__dht__resolve_node_timeout_ms: Option, - pub network__dht__resolve_node_count: u32, - pub network__dht__resolve_node_fanout: u32, - pub network__dht__max_find_node_count: u32, - pub network__dht__get_value_timeout_ms: Option, - pub network__dht__get_value_count: u32, - pub network__dht__get_value_fanout: u32, - pub network__dht__set_value_timeout_ms: Option, - pub network__dht__set_value_count: u32, - pub network__dht__set_value_fanout: u32, - pub network__dht__min_peer_count: u32, - pub network__dht__min_peer_refresh_time_ms: u32, - pub network__dht__validate_dial_info_receipt_time_ms: u32, - // Network / Protocol - // Network / Protocol / UDP - pub network__protocol__udp__enabled: bool, - pub network__protocol__udp__socket_pool_size: u32, - pub network__protocol__udp__listen_address: String, - pub network__protocol__udp__public_address: Option, - // Network / Protocol / TCP - pub network__protocol__tcp__connect: bool, - pub network__protocol__tcp__listen: bool, - pub network__protocol__tcp__max_connections: u32, - pub network__protocol__tcp__listen_address: String, - pub network__protocol__tcp__public_address: Option, - // Network / Protocol / WS - pub network__protocol__ws__connect: bool, - pub network__protocol__ws__listen: bool, - pub network__protocol__ws__max_connections: u32, - pub network__protocol__ws__listen_address: String, - pub network__protocol__ws__path: String, - pub network__protocol__ws__url: Option, - // Network / Protocol / WSS - pub network__protocol__wss__connect: bool, - pub network__protocol__wss__max_connections: u32, - // Network / Leases - pub network__leases__max_server_signal_leases: u32, - pub network__leases__max_server_relay_leases: u32, - pub network__leases__max_client_signal_leases: u32, - pub network__leases__max_client_relay_leases: u32, -} - -cfg_if! { - if #[cfg(target_arch="wasm32")] { - - } else { - - } -} - -cfg_if! { - if #[cfg(target_arch="wasm32")] { - type ConfigReturn = Box; - } else { - type ConfigReturn = Box; - } -} - -impl VeilidConfig { - pub fn get_by_str(&self, key: &str) -> std::result::Result { - let out: ConfigReturn = match key { - "program_name" => Box::new(self.program_name.clone()), - "namespace" => Box::new(self.veilid_namespace.clone()), - "api_log_level" => Box::new(self.api_log_level.to_config_log_level()), - "capabilities.protocol_udp" => Box::new(self.capabilities__protocol_udp.clone()), - "capabilities.protocol_connect_tcp" => { - Box::new(self.capabilities__protocol_connect_tcp.clone()) - } - "capabilities.protocol_accept_tcp" => { - Box::new(self.capabilities__protocol_accept_tcp.clone()) - } - "capabilities.protocol_connect_ws" => { - Box::new(self.capabilities__protocol_connect_ws.clone()) - } - "capabilities.protocol_accept_ws" => { - Box::new(self.capabilities__protocol_accept_ws.clone()) - } - "capabilities.protocol_connect_wss" => { - Box::new(self.capabilities__protocol_connect_wss.clone()) - } - "capabilities.protocol_accept_wss" => { - Box::new(self.capabilities__protocol_accept_wss.clone()) - } - "table_store.directory" => Box::new(self.table_store__directory.clone()), - "table_store.delete" => Box::new(self.table_store__delete.clone()), - "block_store.directory" => Box::new(self.block_store__directory.clone()), - "block_store.delete" => Box::new(self.block_store__delete.clone()), - "protected_store.allow_insecure_fallback" => { - Box::new(self.protected_store__allow_insecure_fallback.clone()) - } - "protected_store.always_use_insecure_storage" => { - Box::new(self.protected_store__always_use_insecure_storage.clone()) - } - "protected_store.insecure_fallback_directory" => { - Box::new(self.protected_store__insecure_fallback_directory.clone()) - } - "protected_store.delete" => Box::new(self.protected_store__delete.clone()), - "network.node_id" => Box::new(self.network__node_id.clone()), - "network.node_id_secret" => Box::new(self.network__node_id_secret.clone()), - "network.max_connections" => Box::new(self.network__max_connections.clone()), - "network.connection_initial_timeout_ms" => { - Box::new(self.network__connection_initial_timeout_ms.clone()) - } - "network.bootstrap" => Box::new(self.network__bootstrap.clone()), - "network.dht.resolve_node_timeout_ms" => { - Box::new(self.network__dht__resolve_node_timeout_ms.clone()) - } - "network.dht.resolve_node_count" => { - Box::new(self.network__dht__resolve_node_count.clone()) - } - "network.dht.resolve_node_fanout" => { - Box::new(self.network__dht__resolve_node_fanout.clone()) - } - "network.dht.max_find_node_count" => { - Box::new(self.network__dht__max_find_node_count.clone()) - } - "network.dht.get_value_timeout_ms" => { - Box::new(self.network__dht__get_value_timeout_ms.clone()) - } - "network.dht.get_value_count" => Box::new(self.network__dht__get_value_count.clone()), - "network.dht.get_value_fanout" => Box::new(self.network__dht__get_value_fanout.clone()), - "network.dht.set_value_timeout_ms" => { - Box::new(self.network__dht__set_value_timeout_ms.clone()) - } - "network.dht.set_value_count" => Box::new(self.network__dht__set_value_count.clone()), - "network.dht.set_value_fanout" => Box::new(self.network__dht__set_value_fanout.clone()), - "network.dht.min_peer_count" => Box::new(self.network__dht__min_peer_count.clone()), - "network.dht.min_peer_refresh_time_ms" => { - Box::new(self.network__dht__min_peer_refresh_time_ms.clone()) - } - "network.dht.validate_dial_info_receipt_time_ms" => Box::new( - self.network__dht__validate_dial_info_receipt_time_ms - .clone(), - ), - "network.rpc.concurrency" => Box::new(self.network__rpc__concurrency.clone()), - "network.rpc.queue_size" => Box::new(self.network__rpc__queue_size.clone()), - "network.rpc.max_timestamp_behind_ms" => { - Box::new(self.network__rpc__max_timestamp_behind_ms.clone()) - } - "network.rpc.max_timestamp_ahead_ms" => { - Box::new(self.network__rpc__max_timestamp_ahead_ms.clone()) - } - "network.rpc.timeout_ms" => Box::new(self.network__rpc__timeout_ms.clone()), - "network.rpc.max_route_hop_count" => { - Box::new(self.network__rpc__max_route_hop_count.clone()) - } - "network.upnp" => Box::new(self.network__upnp.clone()), - "network.natpmp" => Box::new(self.network__natpmp.clone()), - "network.enable_local_peer_scope" => { - Box::new(self.network__enable_local_peer_scope.clone()) - } - "network.restricted_nat_retries" => { - Box::new(self.network__restricted_nat_retries.clone()) - } - "network.tls.certificate_path" => Box::new("".to_owned()), - "network.tls.private_key_path" => Box::new("".to_owned()), - "network.tls.connection_initial_timeout" => Box::new(0u32), - "network.application.https.enabled" => Box::new(false), - "network.application.https.listen_address" => Box::new("".to_owned()), - "network.application.https.path" => Box::new("".to_owned()), - "network.application.https.url" => Box::new(Option::::None), - "network.application.http.enabled" => Box::new(false), - "network.application.http.listen_address" => Box::new("".to_owned()), - "network.application.http.path" => Box::new("".to_owned()), - "network.application.http.url" => Box::new(Option::::None), - "network.protocol.udp.enabled" => { - Box::new(self.network__protocol__udp__enabled.clone()) - } - "network.protocol.udp.socket_pool_size" => { - Box::new(self.network__protocol__udp__socket_pool_size.clone()) - } - "network.protocol.udp.listen_address" => { - Box::new(self.network__protocol__udp__listen_address.clone()) - } - "network.protocol.udp.public_address" => { - Box::new(self.network__protocol__udp__public_address.clone()) - } - "network.protocol.tcp.connect" => { - Box::new(self.network__protocol__tcp__connect.clone()) - } - "network.protocol.tcp.listen" => Box::new(self.network__protocol__tcp__listen.clone()), - "network.protocol.tcp.max_connections" => { - Box::new(self.network__protocol__tcp__max_connections.clone()) - } - "network.protocol.tcp.listen_address" => { - Box::new(self.network__protocol__tcp__listen_address.clone()) - } - "network.protocol.tcp.public_address" => { - Box::new(self.network__protocol__tcp__public_address.clone()) - } - "network.protocol.ws.connect" => Box::new(self.network__protocol__ws__connect.clone()), - "network.protocol.ws.listen" => Box::new(self.network__protocol__ws__listen.clone()), - "network.protocol.ws.max_connections" => { - Box::new(self.network__protocol__ws__max_connections.clone()) - } - "network.protocol.ws.listen_address" => { - Box::new(self.network__protocol__ws__listen_address.clone()) - } - "network.protocol.ws.path" => Box::new(self.network__protocol__ws__path.clone()), - "network.protocol.ws.url" => Box::new(self.network__protocol__ws__url.clone()), - "network.protocol.wss.connect" => { - Box::new(self.network__protocol__wss__connect.clone()) - } - "network.protocol.wss.listen" => Box::new(false), - "network.protocol.wss.max_connections" => { - Box::new(self.network__protocol__wss__max_connections.clone()) - } - "network.protocol.wss.listen_address" => Box::new("".to_owned()), - "network.protocol.wss.path" => Box::new("".to_owned()), - "network.protocol.wss.url" => Box::new(Option::::None), - "network.leases.max_server_signal_leases" => { - Box::new(self.network__leases__max_server_signal_leases.clone()) - } - "network.leases.max_server_relay_leases" => { - Box::new(self.network__leases__max_server_relay_leases.clone()) - } - "network.leases.max_client_signal_leases" => { - Box::new(self.network__leases__max_client_signal_leases.clone()) - } - "network.leases.max_client_relay_leases" => { - Box::new(self.network__leases__max_client_relay_leases.clone()) - } - _ => { - let err = format!("config key '{}' doesn't exist", key); - error!("{}", err); - return Err(err); - } - }; - std::result::Result::Ok(out) - } -} - -///////////////////////////////////////// - -#[derive(Debug)] -pub enum VeilidAPIError { - AlreadyInitialized, - NotInitialized, - InvalidConfig(String), - Timeout, - Shutdown, - NodeNotFound(String), - NoDialInfo(String), - Internal(String), - Unimplemented(String), - ParseError { - message: String, - value: String, - }, - InvalidArgument { - context: String, - argument: String, - value: String, - }, - MissingArgument { - context: String, - argument: String, - }, -} - -impl fmt::Display for VeilidAPIError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - VeilidAPIError::AlreadyInitialized => write!(f, "VeilidAPIError::AlreadyInitialized"), - VeilidAPIError::NotInitialized => write!(f, "VeilidAPIError::NotInitialized"), - VeilidAPIError::InvalidConfig(e) => write!(f, "VeilidAPIError::InvalidConfig({})", e), - VeilidAPIError::Timeout => write!(f, "VeilidAPIError::Timeout"), - VeilidAPIError::Shutdown => write!(f, "VeilidAPIError::Shutdown"), - VeilidAPIError::NodeNotFound(ni) => write!(f, "VeilidAPIError::NodeNotFound({})", ni), - VeilidAPIError::NoDialInfo(ni) => write!(f, "VeilidAPIError::NoDialInfo({})", ni), - VeilidAPIError::Internal(e) => write!(f, "VeilidAPIError::Internal({})", e), - VeilidAPIError::Unimplemented(e) => write!(f, "VeilidAPIError::Unimplemented({})", e), - VeilidAPIError::ParseError { message, value } => { - write!(f, "VeilidAPIError::ParseError({}: {})", message, value) - } - VeilidAPIError::InvalidArgument { - context, - argument, - value, - } => { - write!( - f, - "VeilidAPIError::InvalidArgument({}: {} = {})", - context, argument, value - ) - } - VeilidAPIError::MissingArgument { context, argument } => { - write!( - f, - "VeilidAPIError::MissingArgument({}: {})", - context, argument - ) - } - } - } -} - -impl std::error::Error for VeilidAPIError {} - -impl VeilidAPIError { - fn from_core(api_error: veilid_core::VeilidAPIError) -> Self { - match api_error { - veilid_core::VeilidAPIError::Timeout => VeilidAPIError::Timeout, - veilid_core::VeilidAPIError::Shutdown => VeilidAPIError::Shutdown, - veilid_core::VeilidAPIError::NodeNotFound(node_id) => { - VeilidAPIError::NodeNotFound(format!("{}", node_id)) - } - veilid_core::VeilidAPIError::NoDialInfo(node_id) => { - VeilidAPIError::NodeNotFound(format!("{}", node_id)) - } - veilid_core::VeilidAPIError::Internal(msg) => VeilidAPIError::Internal(msg.clone()), - veilid_core::VeilidAPIError::Unimplemented(msg) => { - VeilidAPIError::Unimplemented(msg.clone()) - } - veilid_core::VeilidAPIError::ParseError { message, value } => { - VeilidAPIError::ParseError { - message: message.clone(), - value: value.clone(), - } - } - veilid_core::VeilidAPIError::InvalidArgument { - context, - argument, - value, - } => VeilidAPIError::InvalidArgument { - context: context.clone(), - argument: argument.clone(), - value: value.clone(), - }, - veilid_core::VeilidAPIError::MissingArgument { context, argument } => { - VeilidAPIError::MissingArgument { - context: context.clone(), - argument: argument.clone(), - } - } - } - } -} - -#[derive(Debug, Clone, Copy)] -pub enum AttachmentState { - Detached, - Attaching, - AttachedWeak, - AttachedGood, - AttachedStrong, - FullyAttached, - OverAttached, - Detaching, -} - -impl AttachmentState { - fn from_core(attachment_state: veilid_core::AttachmentState) -> Self { - match attachment_state { - veilid_core::AttachmentState::Detached => AttachmentState::Detached, - veilid_core::AttachmentState::Attaching => AttachmentState::Attaching, - veilid_core::AttachmentState::AttachedWeak => AttachmentState::AttachedWeak, - veilid_core::AttachmentState::AttachedGood => AttachmentState::AttachedGood, - veilid_core::AttachmentState::AttachedStrong => AttachmentState::AttachedStrong, - veilid_core::AttachmentState::FullyAttached => AttachmentState::FullyAttached, - veilid_core::AttachmentState::OverAttached => AttachmentState::OverAttached, - veilid_core::AttachmentState::Detaching => AttachmentState::Detaching, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub enum VeilidLogLevel { - Error, - Warn, - Info, - Debug, - Trace, -} - -impl Default for VeilidLogLevel { - fn default() -> Self { - Self::Info - } -} - -impl VeilidLogLevel { - fn from_core(level: veilid_core::VeilidLogLevel) -> Self { - match level { - veilid_core::VeilidLogLevel::Error => VeilidLogLevel::Error, - veilid_core::VeilidLogLevel::Warn => VeilidLogLevel::Warn, - veilid_core::VeilidLogLevel::Info => VeilidLogLevel::Info, - veilid_core::VeilidLogLevel::Debug => VeilidLogLevel::Debug, - veilid_core::VeilidLogLevel::Trace => VeilidLogLevel::Trace, - } - } - - fn to_config_log_level(&self) -> veilid_core::VeilidConfigLogLevel { - match self { - Self::Error => veilid_core::VeilidConfigLogLevel::Error, - Self::Warn => veilid_core::VeilidConfigLogLevel::Warn, - Self::Info => veilid_core::VeilidConfigLogLevel::Info, - Self::Debug => veilid_core::VeilidConfigLogLevel::Debug, - Self::Trace => veilid_core::VeilidConfigLogLevel::Trace, - } - } -} - -#[derive(Debug, Clone)] -pub enum VeilidUpdate { - Log { - log_level: VeilidLogLevel, - message: String, - }, - Attachment(AttachmentState), -} - -impl VeilidUpdate { - fn from_core(veilid_update: veilid_core::VeilidUpdate) -> Self { - match veilid_update { - veilid_core::VeilidUpdate::Log { log_level, message } => Self::Log { - log_level: VeilidLogLevel::from_core(log_level), - message, - }, - veilid_core::VeilidUpdate::Attachment(attachment) => { - Self::Attachment(AttachmentState::from_core(attachment)) - } - } - } -} - -#[derive(Debug, Clone)] -pub struct VeilidState { - pub attachment: AttachmentState, -} - -impl VeilidState { - fn from_core(veilid_state: veilid_core::VeilidState) -> Self { - Self { - attachment: AttachmentState::from_core(veilid_state.attachment), - } - } -} - -///////////////////////////////////////// -pub fn startup_veilid_core( - sink: StreamSink, - config: VeilidConfig, -) -> Result { - async_std::task::block_on(async { - let mut api_lock = VEILID_API.lock().await; - if api_lock.is_some() { - return Err(anyhow!(VeilidAPIError::AlreadyInitialized)); - } - - let core = veilid_core::VeilidCore::new(); - - let setup = veilid_core::VeilidCoreSetup { - update_callback: Arc::new( - move |update: veilid_core::VeilidUpdate| -> veilid_core::SystemPinBoxFuture<()> { - let sink = sink.clone(); - Box::pin(async move { - if !sink.add(VeilidUpdate::from_core(update)) { - error!("error sending veilid update callback"); - } - }) - }, - ), - config_callback: Arc::new(move |key| config.get_by_str(&key)), - }; - - let veilid_api = core - .startup(setup) - .await - .map_err(|e| VeilidAPIError::InvalidConfig(e.clone()))?; - *api_lock = Some(veilid_api.clone()); - - let core_state = veilid_api - .get_state() - .await - .map_err(VeilidAPIError::from_core)?; - Ok(VeilidState::from_core(core_state)) - }) -} - -pub fn get_veilid_state() -> Result { - async_std::task::block_on(async { - let veilid_api = get_veilid_api().await?; - let core_state = veilid_api - .get_state() - .await - .map_err(VeilidAPIError::from_core)?; - Ok(VeilidState::from_core(core_state)) - }) -} - -// xxx api functions - -pub fn change_api_log_level(log_level: VeilidLogLevel) -> Result<()> { - async_std::task::block_on(async { - let veilid_api = get_veilid_api().await?; - veilid_api - .change_api_log_level(log_level.to_config_log_level()) - .await; - Ok(()) - }) -} - -pub fn shutdown_veilid_core() -> Result<()> { - async_std::task::block_on(async { - let veilid_api = get_veilid_api().await?; - veilid_api.shutdown().await; - Ok(()) - }) -} - -pub fn veilid_version_string() -> Result { - Ok(veilid_core::veilid_version_string()) -} - -pub struct VeilidVersion { - pub major: u32, - pub minor: u32, - pub patch: u32, -} - -pub fn veilid_version() -> Result { - let (major, minor, patch) = veilid_core::veilid_version(); - Ok(VeilidVersion { - major, - minor, - patch, - }) -} diff --git a/veilid-flutter/rust/src/bridge_generated.rs b/veilid-flutter/rust/src/bridge_generated.rs deleted file mode 100644 index 4588a19c..00000000 --- a/veilid-flutter/rust/src/bridge_generated.rs +++ /dev/null @@ -1,581 +0,0 @@ -#![allow( - non_camel_case_types, - unused, - clippy::redundant_closure, - clippy::useless_conversion, - non_snake_case -)] -// AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`. - -use crate::api::*; -use flutter_rust_bridge::*; - -// Section: wire functions - -#[no_mangle] -pub extern "C" fn wire_startup_veilid_core(port_: i64, config: *mut wire_VeilidConfig) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "startup_veilid_core", - port: Some(port_), - mode: FfiCallMode::Stream, - }, - move || { - let api_config = config.wire2api(); - move |task_callback| startup_veilid_core(task_callback.stream_sink(), api_config) - }, - ) -} - -#[no_mangle] -pub extern "C" fn wire_get_veilid_state(port_: i64) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "get_veilid_state", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || move |task_callback| get_veilid_state(), - ) -} - -#[no_mangle] -pub extern "C" fn wire_change_api_log_level(port_: i64, log_level: i32) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "change_api_log_level", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || { - let api_log_level = log_level.wire2api(); - move |task_callback| change_api_log_level(api_log_level) - }, - ) -} - -#[no_mangle] -pub extern "C" fn wire_shutdown_veilid_core(port_: i64) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "shutdown_veilid_core", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || move |task_callback| shutdown_veilid_core(), - ) -} - -#[no_mangle] -pub extern "C" fn wire_veilid_version_string(port_: i64) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "veilid_version_string", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || move |task_callback| veilid_version_string(), - ) -} - -#[no_mangle] -pub extern "C" fn wire_veilid_version(port_: i64) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "veilid_version", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || move |task_callback| veilid_version(), - ) -} - -// Section: wire structs - -#[repr(C)] -#[derive(Clone)] -pub struct wire_StringList { - ptr: *mut *mut wire_uint_8_list, - len: i32, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_uint_8_list { - ptr: *mut u8, - len: i32, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_VeilidConfig { - program_name: *mut wire_uint_8_list, - veilid_namespace: *mut wire_uint_8_list, - api_log_level: i32, - capabilities__protocol_udp: bool, - capabilities__protocol_connect_tcp: bool, - capabilities__protocol_accept_tcp: bool, - capabilities__protocol_connect_ws: bool, - capabilities__protocol_accept_ws: bool, - capabilities__protocol_connect_wss: bool, - capabilities__protocol_accept_wss: bool, - protected_store__allow_insecure_fallback: bool, - protected_store__always_use_insecure_storage: bool, - protected_store__insecure_fallback_directory: *mut wire_uint_8_list, - protected_store__delete: bool, - table_store__directory: *mut wire_uint_8_list, - table_store__delete: bool, - block_store__directory: *mut wire_uint_8_list, - block_store__delete: bool, - network__max_connections: u32, - network__connection_initial_timeout_ms: u32, - network__node_id: *mut wire_uint_8_list, - network__node_id_secret: *mut wire_uint_8_list, - network__bootstrap: *mut wire_StringList, - network__upnp: bool, - network__natpmp: bool, - network__enable_local_peer_scope: bool, - network__restricted_nat_retries: u32, - network__rpc__concurrency: u32, - network__rpc__queue_size: u32, - network__rpc__max_timestamp_behind_ms: *mut u32, - network__rpc__max_timestamp_ahead_ms: *mut u32, - network__rpc__timeout_ms: u32, - network__rpc__max_route_hop_count: u8, - network__dht__resolve_node_timeout_ms: *mut u32, - network__dht__resolve_node_count: u32, - network__dht__resolve_node_fanout: u32, - network__dht__max_find_node_count: u32, - network__dht__get_value_timeout_ms: *mut u32, - network__dht__get_value_count: u32, - network__dht__get_value_fanout: u32, - network__dht__set_value_timeout_ms: *mut u32, - network__dht__set_value_count: u32, - network__dht__set_value_fanout: u32, - network__dht__min_peer_count: u32, - network__dht__min_peer_refresh_time_ms: u32, - network__dht__validate_dial_info_receipt_time_ms: u32, - network__protocol__udp__enabled: bool, - network__protocol__udp__socket_pool_size: u32, - network__protocol__udp__listen_address: *mut wire_uint_8_list, - network__protocol__udp__public_address: *mut wire_uint_8_list, - network__protocol__tcp__connect: bool, - network__protocol__tcp__listen: bool, - network__protocol__tcp__max_connections: u32, - network__protocol__tcp__listen_address: *mut wire_uint_8_list, - network__protocol__tcp__public_address: *mut wire_uint_8_list, - network__protocol__ws__connect: bool, - network__protocol__ws__listen: bool, - network__protocol__ws__max_connections: u32, - network__protocol__ws__listen_address: *mut wire_uint_8_list, - network__protocol__ws__path: *mut wire_uint_8_list, - network__protocol__ws__url: *mut wire_uint_8_list, - network__protocol__wss__connect: bool, - network__protocol__wss__max_connections: u32, - network__leases__max_server_signal_leases: u32, - network__leases__max_server_relay_leases: u32, - network__leases__max_client_signal_leases: u32, - network__leases__max_client_relay_leases: u32, -} - -// Section: wire enums - -// Section: allocate functions - -#[no_mangle] -pub extern "C" fn new_StringList(len: i32) -> *mut wire_StringList { - let wrap = wire_StringList { - ptr: support::new_leak_vec_ptr(<*mut wire_uint_8_list>::new_with_null_ptr(), len), - len, - }; - support::new_leak_box_ptr(wrap) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_u32(value: u32) -> *mut u32 { - support::new_leak_box_ptr(value) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_veilid_config() -> *mut wire_VeilidConfig { - support::new_leak_box_ptr(wire_VeilidConfig::new_with_null_ptr()) -} - -#[no_mangle] -pub extern "C" fn new_uint_8_list(len: i32) -> *mut wire_uint_8_list { - let ans = wire_uint_8_list { - ptr: support::new_leak_vec_ptr(Default::default(), len), - len, - }; - support::new_leak_box_ptr(ans) -} - -// Section: impl Wire2Api - -pub trait Wire2Api { - fn wire2api(self) -> T; -} - -impl Wire2Api> for *mut S -where - *mut S: Wire2Api, -{ - fn wire2api(self) -> Option { - if self.is_null() { - None - } else { - Some(self.wire2api()) - } - } -} - -impl Wire2Api for *mut wire_uint_8_list { - fn wire2api(self) -> String { - let vec: Vec = self.wire2api(); - String::from_utf8_lossy(&vec).into_owned() - } -} - -impl Wire2Api> for *mut wire_StringList { - fn wire2api(self) -> Vec { - let vec = unsafe { - let wrap = support::box_from_leak_ptr(self); - support::vec_from_leak_ptr(wrap.ptr, wrap.len) - }; - vec.into_iter().map(Wire2Api::wire2api).collect() - } -} - -impl Wire2Api for bool { - fn wire2api(self) -> bool { - self - } -} - -impl Wire2Api for *mut u32 { - fn wire2api(self) -> u32 { - unsafe { *support::box_from_leak_ptr(self) } - } -} - -impl Wire2Api for *mut wire_VeilidConfig { - fn wire2api(self) -> VeilidConfig { - let wrap = unsafe { support::box_from_leak_ptr(self) }; - (*wrap).wire2api().into() - } -} - -impl Wire2Api for u32 { - fn wire2api(self) -> u32 { - self - } -} - -impl Wire2Api for u8 { - fn wire2api(self) -> u8 { - self - } -} - -impl Wire2Api> for *mut wire_uint_8_list { - fn wire2api(self) -> Vec { - unsafe { - let wrap = support::box_from_leak_ptr(self); - support::vec_from_leak_ptr(wrap.ptr, wrap.len) - } - } -} - -impl Wire2Api for wire_VeilidConfig { - fn wire2api(self) -> VeilidConfig { - VeilidConfig { - program_name: self.program_name.wire2api(), - veilid_namespace: self.veilid_namespace.wire2api(), - api_log_level: self.api_log_level.wire2api(), - capabilities__protocol_udp: self.capabilities__protocol_udp.wire2api(), - capabilities__protocol_connect_tcp: self.capabilities__protocol_connect_tcp.wire2api(), - capabilities__protocol_accept_tcp: self.capabilities__protocol_accept_tcp.wire2api(), - capabilities__protocol_connect_ws: self.capabilities__protocol_connect_ws.wire2api(), - capabilities__protocol_accept_ws: self.capabilities__protocol_accept_ws.wire2api(), - capabilities__protocol_connect_wss: self.capabilities__protocol_connect_wss.wire2api(), - capabilities__protocol_accept_wss: self.capabilities__protocol_accept_wss.wire2api(), - protected_store__allow_insecure_fallback: self - .protected_store__allow_insecure_fallback - .wire2api(), - protected_store__always_use_insecure_storage: self - .protected_store__always_use_insecure_storage - .wire2api(), - protected_store__insecure_fallback_directory: self - .protected_store__insecure_fallback_directory - .wire2api(), - protected_store__delete: self.protected_store__delete.wire2api(), - table_store__directory: self.table_store__directory.wire2api(), - table_store__delete: self.table_store__delete.wire2api(), - block_store__directory: self.block_store__directory.wire2api(), - block_store__delete: self.block_store__delete.wire2api(), - network__max_connections: self.network__max_connections.wire2api(), - network__connection_initial_timeout_ms: self - .network__connection_initial_timeout_ms - .wire2api(), - network__node_id: self.network__node_id.wire2api(), - network__node_id_secret: self.network__node_id_secret.wire2api(), - network__bootstrap: self.network__bootstrap.wire2api(), - network__upnp: self.network__upnp.wire2api(), - network__natpmp: self.network__natpmp.wire2api(), - network__enable_local_peer_scope: self.network__enable_local_peer_scope.wire2api(), - network__restricted_nat_retries: self.network__restricted_nat_retries.wire2api(), - network__rpc__concurrency: self.network__rpc__concurrency.wire2api(), - network__rpc__queue_size: self.network__rpc__queue_size.wire2api(), - network__rpc__max_timestamp_behind_ms: self - .network__rpc__max_timestamp_behind_ms - .wire2api(), - network__rpc__max_timestamp_ahead_ms: self - .network__rpc__max_timestamp_ahead_ms - .wire2api(), - network__rpc__timeout_ms: self.network__rpc__timeout_ms.wire2api(), - network__rpc__max_route_hop_count: self.network__rpc__max_route_hop_count.wire2api(), - network__dht__resolve_node_timeout_ms: self - .network__dht__resolve_node_timeout_ms - .wire2api(), - network__dht__resolve_node_count: self.network__dht__resolve_node_count.wire2api(), - network__dht__resolve_node_fanout: self.network__dht__resolve_node_fanout.wire2api(), - network__dht__max_find_node_count: self.network__dht__max_find_node_count.wire2api(), - network__dht__get_value_timeout_ms: self.network__dht__get_value_timeout_ms.wire2api(), - network__dht__get_value_count: self.network__dht__get_value_count.wire2api(), - network__dht__get_value_fanout: self.network__dht__get_value_fanout.wire2api(), - network__dht__set_value_timeout_ms: self.network__dht__set_value_timeout_ms.wire2api(), - network__dht__set_value_count: self.network__dht__set_value_count.wire2api(), - network__dht__set_value_fanout: self.network__dht__set_value_fanout.wire2api(), - network__dht__min_peer_count: self.network__dht__min_peer_count.wire2api(), - network__dht__min_peer_refresh_time_ms: self - .network__dht__min_peer_refresh_time_ms - .wire2api(), - network__dht__validate_dial_info_receipt_time_ms: self - .network__dht__validate_dial_info_receipt_time_ms - .wire2api(), - network__protocol__udp__enabled: self.network__protocol__udp__enabled.wire2api(), - network__protocol__udp__socket_pool_size: self - .network__protocol__udp__socket_pool_size - .wire2api(), - network__protocol__udp__listen_address: self - .network__protocol__udp__listen_address - .wire2api(), - network__protocol__udp__public_address: self - .network__protocol__udp__public_address - .wire2api(), - network__protocol__tcp__connect: self.network__protocol__tcp__connect.wire2api(), - network__protocol__tcp__listen: self.network__protocol__tcp__listen.wire2api(), - network__protocol__tcp__max_connections: self - .network__protocol__tcp__max_connections - .wire2api(), - network__protocol__tcp__listen_address: self - .network__protocol__tcp__listen_address - .wire2api(), - network__protocol__tcp__public_address: self - .network__protocol__tcp__public_address - .wire2api(), - network__protocol__ws__connect: self.network__protocol__ws__connect.wire2api(), - network__protocol__ws__listen: self.network__protocol__ws__listen.wire2api(), - network__protocol__ws__max_connections: self - .network__protocol__ws__max_connections - .wire2api(), - network__protocol__ws__listen_address: self - .network__protocol__ws__listen_address - .wire2api(), - network__protocol__ws__path: self.network__protocol__ws__path.wire2api(), - network__protocol__ws__url: self.network__protocol__ws__url.wire2api(), - network__protocol__wss__connect: self.network__protocol__wss__connect.wire2api(), - network__protocol__wss__max_connections: self - .network__protocol__wss__max_connections - .wire2api(), - network__leases__max_server_signal_leases: self - .network__leases__max_server_signal_leases - .wire2api(), - network__leases__max_server_relay_leases: self - .network__leases__max_server_relay_leases - .wire2api(), - network__leases__max_client_signal_leases: self - .network__leases__max_client_signal_leases - .wire2api(), - network__leases__max_client_relay_leases: self - .network__leases__max_client_relay_leases - .wire2api(), - } - } -} - -impl Wire2Api for i32 { - fn wire2api(self) -> VeilidLogLevel { - match self { - 0 => VeilidLogLevel::Error, - 1 => VeilidLogLevel::Warn, - 2 => VeilidLogLevel::Info, - 3 => VeilidLogLevel::Debug, - 4 => VeilidLogLevel::Trace, - _ => unreachable!("Invalid variant for VeilidLogLevel: {}", self), - } - } -} - -// Section: impl NewWithNullPtr - -pub trait NewWithNullPtr { - fn new_with_null_ptr() -> Self; -} - -impl NewWithNullPtr for *mut T { - fn new_with_null_ptr() -> Self { - std::ptr::null_mut() - } -} - -impl NewWithNullPtr for wire_VeilidConfig { - fn new_with_null_ptr() -> Self { - Self { - program_name: core::ptr::null_mut(), - veilid_namespace: core::ptr::null_mut(), - api_log_level: Default::default(), - capabilities__protocol_udp: Default::default(), - capabilities__protocol_connect_tcp: Default::default(), - capabilities__protocol_accept_tcp: Default::default(), - capabilities__protocol_connect_ws: Default::default(), - capabilities__protocol_accept_ws: Default::default(), - capabilities__protocol_connect_wss: Default::default(), - capabilities__protocol_accept_wss: Default::default(), - protected_store__allow_insecure_fallback: Default::default(), - protected_store__always_use_insecure_storage: Default::default(), - protected_store__insecure_fallback_directory: core::ptr::null_mut(), - protected_store__delete: Default::default(), - table_store__directory: core::ptr::null_mut(), - table_store__delete: Default::default(), - block_store__directory: core::ptr::null_mut(), - block_store__delete: Default::default(), - network__max_connections: Default::default(), - network__connection_initial_timeout_ms: Default::default(), - network__node_id: core::ptr::null_mut(), - network__node_id_secret: core::ptr::null_mut(), - network__bootstrap: core::ptr::null_mut(), - network__upnp: Default::default(), - network__natpmp: Default::default(), - network__enable_local_peer_scope: Default::default(), - network__restricted_nat_retries: Default::default(), - network__rpc__concurrency: Default::default(), - network__rpc__queue_size: Default::default(), - network__rpc__max_timestamp_behind_ms: core::ptr::null_mut(), - network__rpc__max_timestamp_ahead_ms: core::ptr::null_mut(), - network__rpc__timeout_ms: Default::default(), - network__rpc__max_route_hop_count: Default::default(), - network__dht__resolve_node_timeout_ms: core::ptr::null_mut(), - network__dht__resolve_node_count: Default::default(), - network__dht__resolve_node_fanout: Default::default(), - network__dht__max_find_node_count: Default::default(), - network__dht__get_value_timeout_ms: core::ptr::null_mut(), - network__dht__get_value_count: Default::default(), - network__dht__get_value_fanout: Default::default(), - network__dht__set_value_timeout_ms: core::ptr::null_mut(), - network__dht__set_value_count: Default::default(), - network__dht__set_value_fanout: Default::default(), - network__dht__min_peer_count: Default::default(), - network__dht__min_peer_refresh_time_ms: Default::default(), - network__dht__validate_dial_info_receipt_time_ms: Default::default(), - network__protocol__udp__enabled: Default::default(), - network__protocol__udp__socket_pool_size: Default::default(), - network__protocol__udp__listen_address: core::ptr::null_mut(), - network__protocol__udp__public_address: core::ptr::null_mut(), - network__protocol__tcp__connect: Default::default(), - network__protocol__tcp__listen: Default::default(), - network__protocol__tcp__max_connections: Default::default(), - network__protocol__tcp__listen_address: core::ptr::null_mut(), - network__protocol__tcp__public_address: core::ptr::null_mut(), - network__protocol__ws__connect: Default::default(), - network__protocol__ws__listen: Default::default(), - network__protocol__ws__max_connections: Default::default(), - network__protocol__ws__listen_address: core::ptr::null_mut(), - network__protocol__ws__path: core::ptr::null_mut(), - network__protocol__ws__url: core::ptr::null_mut(), - network__protocol__wss__connect: Default::default(), - network__protocol__wss__max_connections: Default::default(), - network__leases__max_server_signal_leases: Default::default(), - network__leases__max_server_relay_leases: Default::default(), - network__leases__max_client_signal_leases: Default::default(), - network__leases__max_client_relay_leases: Default::default(), - } - } -} - -// Section: impl IntoDart - -impl support::IntoDart for AttachmentState { - fn into_dart(self) -> support::DartCObject { - match self { - Self::Detached => 0, - Self::Attaching => 1, - Self::AttachedWeak => 2, - Self::AttachedGood => 3, - Self::AttachedStrong => 4, - Self::FullyAttached => 5, - Self::OverAttached => 6, - Self::Detaching => 7, - } - .into_dart() - } -} - -impl support::IntoDart for VeilidLogLevel { - fn into_dart(self) -> support::DartCObject { - match self { - Self::Error => 0, - Self::Warn => 1, - Self::Info => 2, - Self::Debug => 3, - Self::Trace => 4, - } - .into_dart() - } -} - -impl support::IntoDart for VeilidState { - fn into_dart(self) -> support::DartCObject { - vec![self.attachment.into_dart()].into_dart() - } -} -impl support::IntoDartExceptPrimitive for VeilidState {} - -impl support::IntoDart for VeilidUpdate { - fn into_dart(self) -> support::DartCObject { - match self { - Self::Log { log_level, message } => { - vec![0.into_dart(), log_level.into_dart(), message.into_dart()] - } - Self::Attachment(field0) => vec![1.into_dart(), field0.into_dart()], - } - .into_dart() - } -} - -impl support::IntoDart for VeilidVersion { - fn into_dart(self) -> support::DartCObject { - vec![ - self.major.into_dart(), - self.minor.into_dart(), - self.patch.into_dart(), - ] - .into_dart() - } -} -impl support::IntoDartExceptPrimitive for VeilidVersion {} - -// Section: executor -support::lazy_static! { - pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); -} - -// Section: sync execution mode utility - -#[no_mangle] -pub extern "C" fn free_WireSyncReturnStruct(val: support::WireSyncReturnStruct) { - unsafe { - let _ = support::vec_from_leak_ptr(val.ptr, val.len); - } -} diff --git a/veilid-flutter/rust/src/config.rs b/veilid-flutter/rust/src/config.rs new file mode 100644 index 00000000..f6591356 --- /dev/null +++ b/veilid-flutter/rust/src/config.rs @@ -0,0 +1,243 @@ +use cfg_if::*; +use log::*; +use serde::*; + +///////////////////////////////////////// +// Config Settings +// Not all settings available through Veilid API are available to Flutter applications + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[allow(non_snake_case)] +pub struct VeilidConfig { + pub program_name: String, + pub veilid_namespace: String, + pub api_log_level: veilid_core::VeilidConfigLogLevel, + // Capabilities + pub capabilities__protocol_udp: bool, + pub capabilities__protocol_connect_tcp: bool, + pub capabilities__protocol_accept_tcp: bool, + pub capabilities__protocol_connect_ws: bool, + pub capabilities__protocol_accept_ws: bool, + pub capabilities__protocol_connect_wss: bool, + pub capabilities__protocol_accept_wss: bool, + // Protected Store + pub protected_store__allow_insecure_fallback: bool, + pub protected_store__always_use_insecure_storage: bool, + pub protected_store__insecure_fallback_directory: String, + pub protected_store__delete: bool, + // Table Store + pub table_store__directory: String, + pub table_store__delete: bool, + // Block Store + pub block_store__directory: String, + pub block_store__delete: bool, + // Network + pub network__max_connections: u32, + pub network__connection_initial_timeout_ms: u32, + pub network__node_id: String, + pub network__node_id_secret: String, + pub network__bootstrap: Vec, + pub network__upnp: bool, + pub network__natpmp: bool, + pub network__enable_local_peer_scope: bool, + pub network__restricted_nat_retries: u32, + // Network / RPC + pub network__rpc__concurrency: u32, + pub network__rpc__queue_size: u32, + pub network__rpc__max_timestamp_behind_ms: Option, + pub network__rpc__max_timestamp_ahead_ms: Option, + pub network__rpc__timeout_ms: u32, + pub network__rpc__max_route_hop_count: u8, + // Network / DHT + pub network__dht__resolve_node_timeout_ms: Option, + pub network__dht__resolve_node_count: u32, + pub network__dht__resolve_node_fanout: u32, + pub network__dht__max_find_node_count: u32, + pub network__dht__get_value_timeout_ms: Option, + pub network__dht__get_value_count: u32, + pub network__dht__get_value_fanout: u32, + pub network__dht__set_value_timeout_ms: Option, + pub network__dht__set_value_count: u32, + pub network__dht__set_value_fanout: u32, + pub network__dht__min_peer_count: u32, + pub network__dht__min_peer_refresh_time_ms: u32, + pub network__dht__validate_dial_info_receipt_time_ms: u32, + // Network / Protocol + // Network / Protocol / UDP + pub network__protocol__udp__enabled: bool, + pub network__protocol__udp__socket_pool_size: u32, + pub network__protocol__udp__listen_address: String, + pub network__protocol__udp__public_address: Option, + // Network / Protocol / TCP + pub network__protocol__tcp__connect: bool, + pub network__protocol__tcp__listen: bool, + pub network__protocol__tcp__max_connections: u32, + pub network__protocol__tcp__listen_address: String, + pub network__protocol__tcp__public_address: Option, + // Network / Protocol / WS + pub network__protocol__ws__connect: bool, + pub network__protocol__ws__listen: bool, + pub network__protocol__ws__max_connections: u32, + pub network__protocol__ws__listen_address: String, + pub network__protocol__ws__path: String, + pub network__protocol__ws__url: Option, + // Network / Protocol / WSS + pub network__protocol__wss__connect: bool, + pub network__protocol__wss__max_connections: u32, + // Network / Leases + pub network__leases__max_server_signal_leases: u32, + pub network__leases__max_server_relay_leases: u32, + pub network__leases__max_client_signal_leases: u32, + pub network__leases__max_client_relay_leases: u32, +} + +cfg_if! { + if #[cfg(target_arch="wasm32")] { + type ConfigReturn = Box; + } else { + type ConfigReturn = Box; + } +} + +impl VeilidConfig { + pub fn get_by_str(&self, key: &str) -> std::result::Result { + let out: ConfigReturn = match key { + "program_name" => Box::new(self.program_name.clone()), + "namespace" => Box::new(self.veilid_namespace.clone()), + "api_log_level" => Box::new(self.api_log_level), + "capabilities.protocol_udp" => Box::new(self.capabilities__protocol_udp), + "capabilities.protocol_connect_tcp" => { + Box::new(self.capabilities__protocol_connect_tcp) + } + "capabilities.protocol_accept_tcp" => Box::new(self.capabilities__protocol_accept_tcp), + "capabilities.protocol_connect_ws" => Box::new(self.capabilities__protocol_connect_ws), + "capabilities.protocol_accept_ws" => Box::new(self.capabilities__protocol_accept_ws), + "capabilities.protocol_connect_wss" => { + Box::new(self.capabilities__protocol_connect_wss) + } + "capabilities.protocol_accept_wss" => Box::new(self.capabilities__protocol_accept_wss), + "table_store.directory" => Box::new(self.table_store__directory.clone()), + "table_store.delete" => Box::new(self.table_store__delete), + "block_store.directory" => Box::new(self.block_store__directory.clone()), + "block_store.delete" => Box::new(self.block_store__delete), + "protected_store.allow_insecure_fallback" => { + Box::new(self.protected_store__allow_insecure_fallback) + } + "protected_store.always_use_insecure_storage" => { + Box::new(self.protected_store__always_use_insecure_storage) + } + "protected_store.insecure_fallback_directory" => { + Box::new(self.protected_store__insecure_fallback_directory.clone()) + } + "protected_store.delete" => Box::new(self.protected_store__delete), + "network.node_id" => Box::new(self.network__node_id.clone()), + "network.node_id_secret" => Box::new(self.network__node_id_secret.clone()), + "network.max_connections" => Box::new(self.network__max_connections), + "network.connection_initial_timeout_ms" => { + Box::new(self.network__connection_initial_timeout_ms) + } + "network.bootstrap" => Box::new(self.network__bootstrap.clone()), + "network.dht.resolve_node_timeout_ms" => { + Box::new(self.network__dht__resolve_node_timeout_ms) + } + "network.dht.resolve_node_count" => Box::new(self.network__dht__resolve_node_count), + "network.dht.resolve_node_fanout" => Box::new(self.network__dht__resolve_node_fanout), + "network.dht.max_find_node_count" => Box::new(self.network__dht__max_find_node_count), + "network.dht.get_value_timeout_ms" => Box::new(self.network__dht__get_value_timeout_ms), + "network.dht.get_value_count" => Box::new(self.network__dht__get_value_count), + "network.dht.get_value_fanout" => Box::new(self.network__dht__get_value_fanout), + "network.dht.set_value_timeout_ms" => Box::new(self.network__dht__set_value_timeout_ms), + "network.dht.set_value_count" => Box::new(self.network__dht__set_value_count), + "network.dht.set_value_fanout" => Box::new(self.network__dht__set_value_fanout), + "network.dht.min_peer_count" => Box::new(self.network__dht__min_peer_count), + "network.dht.min_peer_refresh_time_ms" => { + Box::new(self.network__dht__min_peer_refresh_time_ms) + } + "network.dht.validate_dial_info_receipt_time_ms" => { + Box::new(self.network__dht__validate_dial_info_receipt_time_ms) + } + "network.rpc.concurrency" => Box::new(self.network__rpc__concurrency), + "network.rpc.queue_size" => Box::new(self.network__rpc__queue_size), + "network.rpc.max_timestamp_behind_ms" => { + Box::new(self.network__rpc__max_timestamp_behind_ms) + } + "network.rpc.max_timestamp_ahead_ms" => { + Box::new(self.network__rpc__max_timestamp_ahead_ms) + } + "network.rpc.timeout_ms" => Box::new(self.network__rpc__timeout_ms), + "network.rpc.max_route_hop_count" => Box::new(self.network__rpc__max_route_hop_count), + "network.upnp" => Box::new(self.network__upnp), + "network.natpmp" => Box::new(self.network__natpmp), + "network.enable_local_peer_scope" => Box::new(self.network__enable_local_peer_scope), + "network.restricted_nat_retries" => Box::new(self.network__restricted_nat_retries), + "network.tls.certificate_path" => Box::new("".to_owned()), + "network.tls.private_key_path" => Box::new("".to_owned()), + "network.tls.connection_initial_timeout" => Box::new(0u32), + "network.application.https.enabled" => Box::new(false), + "network.application.https.listen_address" => Box::new("".to_owned()), + "network.application.https.path" => Box::new("".to_owned()), + "network.application.https.url" => Box::new(Option::::None), + "network.application.http.enabled" => Box::new(false), + "network.application.http.listen_address" => Box::new("".to_owned()), + "network.application.http.path" => Box::new("".to_owned()), + "network.application.http.url" => Box::new(Option::::None), + "network.protocol.udp.enabled" => Box::new(self.network__protocol__udp__enabled), + "network.protocol.udp.socket_pool_size" => { + Box::new(self.network__protocol__udp__socket_pool_size) + } + "network.protocol.udp.listen_address" => { + Box::new(self.network__protocol__udp__listen_address.clone()) + } + "network.protocol.udp.public_address" => { + Box::new(self.network__protocol__udp__public_address.clone()) + } + "network.protocol.tcp.connect" => Box::new(self.network__protocol__tcp__connect), + "network.protocol.tcp.listen" => Box::new(self.network__protocol__tcp__listen), + "network.protocol.tcp.max_connections" => { + Box::new(self.network__protocol__tcp__max_connections) + } + "network.protocol.tcp.listen_address" => { + Box::new(self.network__protocol__tcp__listen_address.clone()) + } + "network.protocol.tcp.public_address" => { + Box::new(self.network__protocol__tcp__public_address.clone()) + } + "network.protocol.ws.connect" => Box::new(self.network__protocol__ws__connect), + "network.protocol.ws.listen" => Box::new(self.network__protocol__ws__listen), + "network.protocol.ws.max_connections" => { + Box::new(self.network__protocol__ws__max_connections) + } + "network.protocol.ws.listen_address" => { + Box::new(self.network__protocol__ws__listen_address.clone()) + } + "network.protocol.ws.path" => Box::new(self.network__protocol__ws__path.clone()), + "network.protocol.ws.url" => Box::new(self.network__protocol__ws__url.clone()), + "network.protocol.wss.connect" => Box::new(self.network__protocol__wss__connect), + "network.protocol.wss.listen" => Box::new(false), + "network.protocol.wss.max_connections" => { + Box::new(self.network__protocol__wss__max_connections) + } + "network.protocol.wss.listen_address" => Box::new("".to_owned()), + "network.protocol.wss.path" => Box::new("".to_owned()), + "network.protocol.wss.url" => Box::new(Option::::None), + "network.leases.max_server_signal_leases" => { + Box::new(self.network__leases__max_server_signal_leases) + } + "network.leases.max_server_relay_leases" => { + Box::new(self.network__leases__max_server_relay_leases) + } + "network.leases.max_client_signal_leases" => { + Box::new(self.network__leases__max_client_signal_leases) + } + "network.leases.max_client_relay_leases" => { + Box::new(self.network__leases__max_client_relay_leases) + } + _ => { + let err = format!("config key '{}' doesn't exist", key); + error!("{}", err); + return Err(err); + } + }; + std::result::Result::Ok(out) + } +} diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs new file mode 100644 index 00000000..d9131426 --- /dev/null +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -0,0 +1,177 @@ +use crate::config::*; +use crate::dart_isolate_wrapper::*; +use crate::dart_serialize::*; + +use allo_isolate::*; +use async_std::sync::Mutex as AsyncMutex; +use ffi_support::*; +use lazy_static::*; +use log::*; +use std::os::raw::c_char; +use std::sync::Arc; + +// Globals + +lazy_static! { + static ref VEILID_API: AsyncMutex> = AsyncMutex::new(None); +} + +async fn get_veilid_api() -> Result { + let api_lock = VEILID_API.lock().await; + api_lock + .as_ref() + .cloned() + .ok_or(veilid_core::VeilidAPIError::NotInitialized) +} + +async fn take_veilid_api() -> Result { + let mut api_lock = VEILID_API.lock().await; + api_lock + .take() + .ok_or(veilid_core::VeilidAPIError::NotInitialized) +} + +///////////////////////////////////////// +// FFI Helpers + +// Declare external routine to release ffi strings +define_string_destructor!(free_string); + +// Utility types for async API results +type APIResult = Result; +const APIRESULT_VOID: APIResult<()> = APIResult::Ok(()); + +// Stream abort macro for simplified error handling +macro_rules! check_err_json { + ($stream:expr, $ex:expr) => { + match $ex { + Ok(v) => v, + Err(e) => { + $stream.abort_json(e); + return; + } + } + }; +} + +///////////////////////////////////////// +// Initializer +#[no_mangle] +pub extern "C" fn initialize_veilid_flutter(dart_post_c_object_ptr: ffi::DartPostCObjectFnType) { + unsafe { + store_dart_post_cobject(dart_post_c_object_ptr); + } + + use std::sync::Once; + static INIT_BACKTRACE: Once = Once::new(); + INIT_BACKTRACE.call_once(move || { + std::env::set_var("RUST_BACKTRACE", "1"); + std::panic::set_hook(Box::new(move |panic_info| { + let (file, line) = if let Some(loc) = panic_info.location() { + (loc.file(), loc.line()) + } else { + ("", 0) + }; + log::error!("### Rust `panic!` hit at file '{}', line {}", file, line); + if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + error!("panic payload: {:?}", s); + } else if let Some(s) = panic_info.payload().downcast_ref::() { + error!("panic payload: {:?}", s); + } else if let Some(a) = panic_info.payload().downcast_ref::() { + error!("panic payload: {:?}", a); + } else { + error!("no panic payload"); + } + log::error!(" Complete stack trace:\n{:?}", backtrace::Backtrace::new()); + + // And stop the process, no recovery is going to be possible here + std::process::abort(); + })); + }); +} + +////////////////////////////////////////////////////////////////////////////////// +/// C-compatible FFI Functions + +#[no_mangle] +pub extern "C" fn startup_veilid_core(port: i64, config: FfiStr) { + let config = config.into_opt_string(); + let stream = DartIsolateStream::new(port); + async_std::task::spawn(async move { + let config: VeilidConfig = check_err_json!(stream, deserialize_opt_json(config)); + + let mut api_lock = VEILID_API.lock().await; + if api_lock.is_some() { + stream.abort_json(veilid_core::VeilidAPIError::AlreadyInitialized); + return; + } + + let sink = stream.clone(); + let setup = veilid_core::VeilidCoreSetup { + update_callback: Arc::new( + move |update: veilid_core::VeilidUpdate| -> veilid_core::SystemPinBoxFuture<()> { + let sink = sink.clone(); + Box::pin(async move { + sink.item_json(update); + }) + }, + ), + config_callback: Arc::new(move |key| config.get_by_str(&key)), + }; + + let res = veilid_core::api_startup(setup).await; + let veilid_api = check_err_json!(stream, res); + *api_lock = Some(veilid_api); + }); +} + +#[no_mangle] +pub extern "C" fn get_veilid_state(port: i64) { + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = get_veilid_api().await?; + let core_state = veilid_api.get_state().await?; + APIResult::Ok(core_state) + }); +} + +#[no_mangle] +pub extern "C" fn change_api_log_level(port: i64, log_level: FfiStr) { + let log_level = log_level.into_opt_string(); + DartIsolateWrapper::new(port).spawn_result_json(async move { + let log_level: veilid_core::VeilidConfigLogLevel = deserialize_opt_json(log_level)?; + let veilid_api = get_veilid_api().await?; + veilid_api.change_api_log_level(log_level).await; + APIRESULT_VOID + }); +} + +#[no_mangle] +pub extern "C" fn shutdown_veilid_core(port: i64) { + DartIsolateWrapper::new(port).spawn_result_json(async move { + let veilid_api = take_veilid_api().await?; + veilid_api.shutdown().await; + APIRESULT_VOID + }); +} + +#[no_mangle] +pub extern "C" fn veilid_version_string() -> *mut c_char { + veilid_core::veilid_version_string().into_ffi_value() +} + +#[repr(C)] +pub struct VeilidVersion { + pub major: u32, + pub minor: u32, + pub patch: u32, +} + +#[no_mangle] +pub extern "C" fn veilid_version() -> VeilidVersion { + let (major, minor, patch) = veilid_core::veilid_version(); + VeilidVersion { + major, + minor, + patch, + } +} diff --git a/veilid-flutter/rust/src/dart_isolate_wrapper.rs b/veilid-flutter/rust/src/dart_isolate_wrapper.rs new file mode 100644 index 00000000..365b9b5c --- /dev/null +++ b/veilid-flutter/rust/src/dart_isolate_wrapper.rs @@ -0,0 +1,151 @@ +use crate::dart_serialize::*; +pub use allo_isolate::ffi::DartCObject; +pub use allo_isolate::IntoDart; +use allo_isolate::Isolate; +use core::future::Future; +use parking_lot::Mutex; +use serde::*; +use std::sync::Arc; + +#[derive(Clone)] +pub struct DartIsolateWrapper { + isolate: Isolate, +} + +const MESSAGE_OK: i32 = 0; +const MESSAGE_ERR: i32 = 1; +const MESSAGE_OK_JSON: i32 = 2; +const MESSAGE_ERR_JSON: i32 = 3; +const MESSAGE_STREAM_ITEM: i32 = 4; +const MESSAGE_STREAM_ITEM_JSON: i32 = 5; +const MESSAGE_STREAM_ABORT: i32 = 6; +const MESSAGE_STREAM_ABORT_JSON: i32 = 7; +const MESSAGE_STREAM_CLOSE: i32 = 8; + +impl DartIsolateWrapper { + pub fn new(port: i64) -> Self { + DartIsolateWrapper { + isolate: Isolate::new(port), + } + } + + pub fn spawn_result_json(self, future: F) + where + F: Future> + Send + 'static, + T: Serialize, + E: Serialize, + { + async_std::task::spawn(async move { + self.result_json(future.await); + }); + } + + pub fn result(&self, result: Result) -> bool { + match result { + Ok(v) => self.ok(v), + Err(e) => self.err(e), + } + } + pub fn result_json(&self, result: Result) -> bool { + match result { + Ok(v) => self.ok_json(v), + Err(e) => self.err_json(e), + } + } + pub fn ok(&self, value: T) -> bool { + self.isolate + .post(vec![MESSAGE_OK.into_dart(), value.into_dart()]) + } + + pub fn ok_json(&self, value: T) -> bool { + self.isolate.post(vec![ + MESSAGE_OK_JSON.into_dart(), + serialize_json(value).into_dart(), + ]) + } + + pub fn err(&self, error: E) -> bool { + self.isolate + .post(vec![MESSAGE_ERR.into_dart(), error.into_dart()]) + } + + pub fn err_json(&self, error: E) -> bool { + self.isolate.post(vec![ + MESSAGE_ERR_JSON.into_dart(), + serialize_json(error).into_dart(), + ]) + } +} + +#[derive(Clone)] +pub struct DartIsolateStream { + isolate: Arc>>, +} + +impl DartIsolateStream { + pub fn new(port: i64) -> Self { + DartIsolateStream { + isolate: Arc::new(Mutex::new(Some(Isolate::new(port)))), + } + } + + pub fn item(&self, value: T) -> bool { + let isolate = self.isolate.lock(); + if let Some(isolate) = &*isolate { + isolate.post(vec![MESSAGE_STREAM_ITEM.into_dart(), value.into_dart()]) + } else { + false + } + } + + pub fn item_json(&self, value: T) -> bool { + let isolate = self.isolate.lock(); + if let Some(isolate) = &*isolate { + isolate.post(vec![ + MESSAGE_STREAM_ITEM_JSON.into_dart(), + serialize_json(value).into_dart(), + ]) + } else { + false + } + } + + pub fn abort(self, error: E) -> bool { + let mut isolate = self.isolate.lock(); + if let Some(isolate) = isolate.take() { + isolate.post(vec![MESSAGE_STREAM_ABORT.into_dart(), error.into_dart()]) + } else { + false + } + } + + pub fn abort_json(self, error: E) -> bool { + let mut isolate = self.isolate.lock(); + if let Some(isolate) = isolate.take() { + isolate.post(vec![ + MESSAGE_STREAM_ABORT_JSON.into_dart(), + serialize_json(error).into_dart(), + ]) + } else { + false + } + } + + pub fn close(self) -> bool { + let mut isolate = self.isolate.lock(); + if let Some(isolate) = isolate.take() { + isolate.post(vec![MESSAGE_STREAM_CLOSE.into_dart()]) + } else { + false + } + } +} + +impl Drop for DartIsolateStream { + fn drop(&mut self) { + let mut isolate = self.isolate.lock(); + if let Some(isolate) = isolate.take() { + isolate.post(vec![MESSAGE_STREAM_CLOSE.into_dart()]); + } + } +} diff --git a/veilid-flutter/rust/src/dart_serialize.rs b/veilid-flutter/rust/src/dart_serialize.rs new file mode 100644 index 00000000..1124b46e --- /dev/null +++ b/veilid-flutter/rust/src/dart_serialize.rs @@ -0,0 +1,24 @@ +use serde::*; + +pub fn deserialize_json<'a, T: de::Deserialize<'a>>( + arg: &'a str, +) -> Result { + serde_json::from_str(arg).map_err(|e| veilid_core::VeilidAPIError::ParseError { + message: e.to_string(), + value: String::new(), + }) +} + +pub fn deserialize_opt_json( + arg: Option, +) -> Result { + let arg = arg.ok_or_else(|| veilid_core::VeilidAPIError::ParseError { + message: "invalid null string passed to rust".to_owned(), + value: String::new(), + })?; + deserialize_json(&arg) +} + +pub fn serialize_json(val: T) -> String { + serde_json::to_string(&val).expect("failed to serialize json value") +} diff --git a/veilid-flutter/rust/src/lib.rs b/veilid-flutter/rust/src/lib.rs index 5cf58a4e..0598cd0c 100644 --- a/veilid-flutter/rust/src/lib.rs +++ b/veilid-flutter/rust/src/lib.rs @@ -1,5 +1,15 @@ -mod api; -mod bridge_generated; +use cfg_if::*; + +cfg_if! { + if #[cfg(not(target_arch = "wasm32"))] { + mod dart_ffi; + mod dart_isolate_wrapper; + mod dart_serialize; + } else { + mod wasm; + } +} +mod config; #[cfg(target_os = "android")] use jni::{objects::JClass, objects::JObject, JNIEnv}; diff --git a/veilid-flutter/rust/src/wasm/mod.rs b/veilid-flutter/rust/src/wasm/mod.rs new file mode 100644 index 00000000..86e8a845 --- /dev/null +++ b/veilid-flutter/rust/src/wasm/mod.rs @@ -0,0 +1,25 @@ +#![cfg(target_arch = "wasm32")] +#![no_std] + +#[macro_use] +extern crate alloc; + +pub use log::*; +pub use wasm_bindgen::prelude::*; +pub use wasm_bindgen::JsCast; + +pub use alloc::boxed::Box; +pub use alloc::string::String; +pub use alloc::sync::Arc; +pub use alloc::vec::Vec; +pub use core::convert::TryFrom; +pub use js_sys::*; +pub use js_veilid_core::*; +pub use utils::*; +pub use veilid_core::dht::key::*; +pub use veilid_core::xx::*; +pub use veilid_core::*; +pub use wasm_logger::*; + +mod js_veilid_core; +mod utils; diff --git a/veilid-server/src/client_api.rs b/veilid-server/src/client_api.rs index d1b14137..fd050c47 100644 --- a/veilid-server/src/client_api.rs +++ b/veilid-server/src/client_api.rs @@ -36,7 +36,10 @@ fn convert_update( rpc_update: crate::veilid_client_capnp::veilid_update::Builder, ) { match update { - veilid_core::VeilidUpdate::Log(_ll, _s) => { + veilid_core::VeilidUpdate::Log { + log_level: _, + message: _, + } => { panic!("Should not be logging to api in server!"); } veilid_core::VeilidUpdate::Attachment(state) => { diff --git a/veilid-server/src/server.rs b/veilid-server/src/server.rs index 8d187d14..2194d12b 100644 --- a/veilid-server/src/server.rs +++ b/veilid-server/src/server.rs @@ -24,9 +24,6 @@ pub fn shutdown() { pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(), String> { let settingsr = settings.read(); - // Create Veilid Core - let veilid_core = veilid_core::VeilidCore::new(); - // Create client api state change pipe let (sender, receiver): ( Sender, @@ -49,8 +46,7 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<( }; // Start Veilid Core and get API - let veilid_api = veilid_core - .startup(vcs) + let veilid_api = veilid_core::api_startup(vcs) .await .map_err(|e| format!("VeilidCore startup failed: {}", e))?; diff --git a/veilid-wasm/.gitignore b/veilid-wasm/.gitignore new file mode 100644 index 00000000..1711838e --- /dev/null +++ b/veilid-wasm/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +bin/ +pkg/ +wasm-pack.log diff --git a/veilid-wasm/Cargo.toml b/veilid-wasm/Cargo.toml new file mode 100644 index 00000000..812ec6d9 --- /dev/null +++ b/veilid-wasm/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "veilid-wasm" +version = "0.1.0" +authors = ["John Smith "] +edition = "2021" +license = "LGPL-2.0-or-later OR MPL-2.0 OR (MIT AND BSD-3-Clause)" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +wasm-bindgen = "^0" +console_error_panic_hook = "^0" +wee_alloc = "^0" +wasm-logger = "^0" +log = "^0" +veilid-core = { path = "../veilid-core" } +cfg-if = "^1" +wasm-bindgen-futures = "^0" +js-sys = "^0" + +[dev-dependencies] +wasm-bindgen-test = "^0" diff --git a/veilid-wasm/src/js_veilid_core.rs b/veilid-wasm/src/js_veilid_core.rs new file mode 100644 index 00000000..53475710 --- /dev/null +++ b/veilid-wasm/src/js_veilid_core.rs @@ -0,0 +1,286 @@ +use crate::*; +pub use wasm_bindgen_futures::*; + +#[wasm_bindgen(js_name = VeilidStateChange)] +pub struct JsVeilidStateChange { + kind: String, // "attachment" => AttachmentState(String) + from: JsValue, + to: JsValue, +} +#[wasm_bindgen(js_name = VeilidState)] +pub struct JsVeilidState { + kind: String, // "attachment" => AttachmentState(String) + state: JsValue, +} + +#[wasm_bindgen(js_name = VeilidCore)] +pub struct JsVeilidCore { + core: VeilidCore, +} + +#[wasm_bindgen(js_class = VeilidCore)] +impl JsVeilidCore { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + set_panic_hook(); + JsVeilidCore { + core: VeilidCore::new(), + } + } + fn value_to_string(val: JsValue) -> Result, ()> { + Ok(Box::new(val.as_string().ok_or(())?)) + } + fn value_to_option_string(val: JsValue) -> Result, ()> { + if val.is_null() || val.is_undefined() { + return Ok(Box::new(Option::::None)); + } + Ok(Box::new(Some(val.as_string().ok_or(())?))) + } + fn value_to_bool(val: JsValue) -> Result, ()> { + Ok(Box::new(val.is_truthy())) + } + fn value_to_u8(val: JsValue) -> Result, ()> { + Ok(Box::new(f64_try_to_unsigned::( + val.as_f64().ok_or(())?, + )?)) + } + fn value_to_u32(val: JsValue) -> Result, ()> { + Ok(Box::new(f64_try_to_unsigned::( + val.as_f64().ok_or(())?, + )?)) + } + fn value_to_u64(val: JsValue) -> Result, ()> { + Ok(Box::new(f64_try_to_unsigned::( + val.as_f64().ok_or(())?, + )?)) + } + fn value_to_option_u64(val: JsValue) -> Result, ()> { + if val.is_null() || val.is_undefined() { + return Ok(Box::new(Option::::None)); + } + + Ok(Box::new(Some(f64_try_to_unsigned::( + val.as_f64().ok_or(())?, + )?))) + } + fn value_to_dht_key(val: JsValue) -> Result, ()> { + Ok(Box::new( + DHTKey::try_decode(val.as_string().ok_or(())?.as_str()).map_err(drop)?, + )) + } + fn value_to_dht_key_secret(val: JsValue) -> Result, ()> { + Ok(Box::new( + DHTKeySecret::try_decode(val.as_string().ok_or(())?.as_str()).map_err(drop)?, + )) + } + fn value_to_vec_string(val: JsValue) -> Result, ()> { + let arrval = val.dyn_into::().map_err(drop)?.to_vec(); + let mut out = Vec::::with_capacity(arrval.len()); + for v in arrval { + out.push(v.as_string().ok_or(())?); + } + Ok(Box::new(out)) + } + + fn translate_config_callback(key: &str, val: JsValue) -> Result, ()> { + match key { + // xxx: lots of missing keys here + "namespace" => Self::value_to_string(val), + "capabilities.protocol_udp" => Self::value_to_bool(val), + "capabilities.protocol_connect_tcp" => Self::value_to_bool(val), + "capabilities.protocol_accept_tcp" => Self::value_to_bool(val), + "capabilities.protocol_connect_ws" => Self::value_to_bool(val), + "capabilities.protocol_accept_ws" => Self::value_to_bool(val), + "capabilities.protocol_connect_wss" => Self::value_to_bool(val), + "capabilities.protocol_accept_wss" => Self::value_to_bool(val), + "tablestore.directory" => Self::value_to_string(val), + "network.max_connections" => Self::value_to_u32(val), + "network.node_id" => Self::value_to_dht_key(val), + "network.node_id_secret" => Self::value_to_dht_key_secret(val), + "network.bootstrap" => Self::value_to_vec_string(val), + "network.rpc.concurrency" => Self::value_to_u32(val), + "network.rpc.queue_size" => Self::value_to_u32(val), + "network.rpc.max_timestamp_behind" => Self::value_to_option_u64(val), + "network.rpc.max_timestamp_ahead" => Self::value_to_option_u64(val), + "network.rpc.timeout" => Self::value_to_u64(val), + "network.rpc.max_route_hop_count" => Self::value_to_u8(val), + "network.dht.resolve_node_timeout" => Self::value_to_option_u64(val), + "network.dht.resolve_node_count" => Self::value_to_u32(val), + "network.dht.resolve_node_fanout" => Self::value_to_u32(val), + "network.dht.max_find_node_count" => Self::value_to_u32(val), + "network.dht.get_value_timeout" => Self::value_to_option_u64(val), + "network.dht.get_value_count" => Self::value_to_u32(val), + "network.dht.get_value_fanout" => Self::value_to_u32(val), + "network.dht.set_value_timeout" => Self::value_to_option_u64(val), + "network.dht.set_value_count" => Self::value_to_u32(val), + "network.dht.set_value_fanout" => Self::value_to_u32(val), + "network.dht.min_peer_count" => Self::value_to_u32(val), + "network.dht.min_peer_refresh_time" => Self::value_to_u64(val), + "network.dht.validate_dial_info_receipt_time" => Self::value_to_u64(val), + "network.upnp" => Self::value_to_bool(val), + "network.natpmp" => Self::value_to_bool(val), + "network.address_filter" => Self::value_to_bool(val), + "network.restricted_nat_retries" => Self::value_to_u32(val), + "network.tls.certificate_path" => Self::value_to_string(val), + "network.tls.private_key_path" => Self::value_to_string(val), + "network.application.path" => Self::value_to_string(val), + "network.application.https.enabled" => Self::value_to_bool(val), + "network.application.https.listen_address" => Self::value_to_string(val), + "network.application.http.enabled" => Self::value_to_bool(val), + "network.application.http.listen_address" => Self::value_to_string(val), + "network.protocol.udp.enabled" => Self::value_to_bool(val), + "network.protocol.udp.socket_pool_size" => Self::value_to_u32(val), + "network.protocol.udp.listen_address" => Self::value_to_string(val), + "network.protocol.udp.public_address" => Self::value_to_option_string(val), + "network.protocol.tcp.connect" => Self::value_to_bool(val), + "network.protocol.tcp.listen" => Self::value_to_bool(val), + "network.protocol.tcp.max_connections" => Self::value_to_u32(val), + "network.protocol.tcp.listen_address" => Self::value_to_string(val), + "network.protocol.tcp.public_address" => Self::value_to_option_string(val), + "network.protocol.ws.connect" => Self::value_to_bool(val), + "network.protocol.ws.listen" => Self::value_to_bool(val), + "network.protocol.ws.max_connections" => Self::value_to_u32(val), + "network.protocol.ws.listen_address" => Self::value_to_string(val), + "network.protocol.ws.path" => Self::value_to_string(val), + "network.protocol.ws.public_address" => Self::value_to_option_string(val), + "network.protocol.wss.connect" => Self::value_to_bool(val), + "network.protocol.wss.listen" => Self::value_to_bool(val), + "network.protocol.wss.max_connections" => Self::value_to_u32(val), + "network.protocol.wss.listen_address" => Self::value_to_string(val), + "network.protocol.wss.path" => Self::value_to_string(val), + "network.protocol.wss.public_address" => Self::value_to_option_string(val), + _ => return Err(()), + } + } + fn translate_veilid_state(state: JsVeilidState) -> Result { + Ok(match state.kind.as_str() { + "attachment" => { + let state_string = state + .state + .as_string() + .ok_or(JsValue::from_str("state should be a string"))?; + let astate = AttachmentState::try_from(state_string) + .map_err(|e| JsValue::from_str(format!("invalid state: {:?}", e).as_str()))?; + VeilidState::Attachment(astate) + } + _ => return Err(JsValue::from_str("unknown state kind")), + }) + } + // xxx rework this for new veilid_api mechanism which should be its own js object now + pub fn startup( + &self, + js_state_change_callback: Function, + js_config_callback: Function, + ) -> Promise { + let core = self.core.clone(); + future_to_promise(async move { + let vcs = VeilidCoreSetup { + state_change_callback: Arc::new( + move |change: VeilidStateChange| -> SystemPinBoxFuture<()> { + let js_state_change_callback = js_state_change_callback.clone(); + Box::pin(async move { + let js_change = match change { + VeilidStateChange::Attachment { + old_state, + new_state, + } => JsVeilidStateChange { + kind: "attachment".to_owned(), + from: JsValue::from_str(old_state.to_string().as_str()), + to: JsValue::from_str(new_state.to_string().as_str()), + }, + }; + + let ret = match Function::call1( + &js_state_change_callback, + &JsValue::UNDEFINED, + &JsValue::from(js_change), + ) { + Ok(v) => v, + Err(e) => { + error!("calling state change callback failed: {:?}", e); + return; + } + }; + let retp: Promise = match ret.dyn_into() { + Ok(v) => v, + Err(e) => { + error!( + "state change callback did not return a promise: {:?}", + e + ); + return; + } + }; + match JsFuture::from(retp).await { + Ok(_) => (), + Err(e) => { + error!("state change callback returned an error: {:?}", e); + return; + } + }; + }) + }, + ), + config_callback: Arc::new( + move |key: String| -> Result, String> { + let val = Function::call1( + &js_config_callback, + &JsValue::UNDEFINED, + &JsValue::from_str(key.as_str()), + ) + .map_err(|_| { + format!("Failed to get config from callback for key '{}'", key) + })?; + + Self::translate_config_callback(key.as_str(), val) + .map_err(|_| format!("invalid value type for config key '{}'", key)) + }, + ), + }; + + match core.startup(vcs).await { + Ok(_) => Ok(JsValue::UNDEFINED), + Err(e) => Err(JsValue::from_str( + format!("VeilidCore startup() failed: {}", e.to_string()).as_str(), + )), + } + }) + } + + pub fn send_state_update(&self) { + self.core.send_state_update(); + } + + pub fn shutdown(&self) -> Promise { + let core = self.core.clone(); + future_to_promise(async move { + core.shutdown().await; + Ok(JsValue::UNDEFINED) + }) + } + + pub fn attach(&self) -> Promise { + let core = self.core.clone(); + future_to_promise(async move { + core.attach(); + Ok(JsValue::UNDEFINED) + }) + } + + pub fn detach(&self) -> Promise { + let core = self.core.clone(); + future_to_promise(async move { + core.detach(); + Ok(JsValue::UNDEFINED) + }) + } + + pub fn wait_for_state(&self, state: JsVeilidState) -> Promise { + let core = self.core.clone(); + future_to_promise(async move { + let state = Self::translate_veilid_state(state)?; + core.wait_for_state(state).await; + Ok(JsValue::UNDEFINED) + }) + } +} diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs new file mode 100644 index 00000000..86e8a845 --- /dev/null +++ b/veilid-wasm/src/lib.rs @@ -0,0 +1,25 @@ +#![cfg(target_arch = "wasm32")] +#![no_std] + +#[macro_use] +extern crate alloc; + +pub use log::*; +pub use wasm_bindgen::prelude::*; +pub use wasm_bindgen::JsCast; + +pub use alloc::boxed::Box; +pub use alloc::string::String; +pub use alloc::sync::Arc; +pub use alloc::vec::Vec; +pub use core::convert::TryFrom; +pub use js_sys::*; +pub use js_veilid_core::*; +pub use utils::*; +pub use veilid_core::dht::key::*; +pub use veilid_core::xx::*; +pub use veilid_core::*; +pub use wasm_logger::*; + +mod js_veilid_core; +mod utils; diff --git a/veilid-wasm/src/utils.rs b/veilid-wasm/src/utils.rs new file mode 100644 index 00000000..0294b6ba --- /dev/null +++ b/veilid-wasm/src/utils.rs @@ -0,0 +1,38 @@ +use cfg_if::*; +use wasm_bindgen::prelude::*; +//use wasm_bindgen_futures::*; + +cfg_if! { + if #[cfg(feature = "wee_alloc")] { + // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global + // allocator. + extern crate wee_alloc; + #[global_allocator] + static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + } +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console, js_name = log)] + pub fn console_log(s: &str); + + #[wasm_bindgen] + pub fn alert(s: &str); +} + +pub fn set_panic_hook() { + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} + +pub fn f64_try_to_unsigned(f: f64) -> Result +where + T: core::convert::TryFrom, +{ + let rf = f.floor(); + if rf < 0.0 { + return Err(()); + } + T::try_from(rf as u64).map_err(drop) +} diff --git a/veilid-wasm/tests/web.rs b/veilid-wasm/tests/web.rs new file mode 100644 index 00000000..1afc7455 --- /dev/null +++ b/veilid-wasm/tests/web.rs @@ -0,0 +1,179 @@ +//! Test suite for the Web and headless browsers. +#![cfg(target_arch = "wasm32")] + +extern crate alloc; +extern crate wasm_bindgen_test; +use core::sync::atomic::AtomicBool; +use veilid_wasm::*; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +static SETUP_ONCE: AtomicBool = AtomicBool::new(false); +pub fn setup() -> () { + if SETUP_ONCE + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + console_log("setup()"); + console_error_panic_hook::set_once(); + wasm_logger::init(wasm_logger::Config::new(Level::Trace)); + init_callbacks(); + } +} +// xxx needs updating to new keys and veilid_api object +fn init_callbacks() { + assert_eq!(js_sys::eval(r#" + window.sleep = (milliseconds) => { return new Promise(resolve => setTimeout(resolve, milliseconds)) }; + window.stateChangeCallback = async (stateChange) => { console.log("State change: " + stateChange); }; + window.configCallback = (configKey) => { + switch(configKey) { + case "namespace": return ""; + case "capabilities.protocol_udp": return false; + case "capabilities.protocol_connect_tcp": return false; + case "capabilities.protocol_accept_tcp": return false; + case "capabilities.protocol_connect_ws": return true; + case "capabilities.protocol_accept_ws": return false; + case "capabilities.protocol_connect_wss": return true; + case "capabilities.protocol_accept_wss": return false; + case "tablestore.directory": return ""; + case "network.max_connections": return 16; + case "network.node_id": return "ZLd4uMYdP4qYLtxF6GqrzBb32Z6T3rE2FWMkWup1pdY"; + case "network.node_id_secret": return "s2Gvq6HJOxgQh-3xIgfWSL3I-DWZ2c1RjZLJl2Xmg2E"; + case "network.bootstrap": return []; + case "network.rpc.concurrency": return 2; + case "network.rpc.queue_size": return 128; + case "network.rpc.max_timestamp_behind": return 10000000; + case "network.rpc.max_timestamp_ahead": return 10000000; + case "network.rpc.timeout": return 10000000; + case "network.rpc.max_route_hop_count": return 7; + case "network.dht.resolve_node_timeout": return null; + case "network.dht.resolve_node_count": return 20; + case "network.dht.resolve_node_fanout": return 3; + case "network.dht.max_find_node_count": return 20; + case "network.dht.get_value_timeout": return null; + case "network.dht.get_value_count": return 20; + case "network.dht.get_value_fanout": return 3; + case "network.dht.set_value_timeout": return null; + case "network.dht.set_value_count": return 20; + case "network.dht.set_value_fanout": return 5; + case "network.dht.min_peer_count": return 20; + case "network.dht.min_peer_refresh_time": return 2000000; + case "network.dht.validate_dial_info_receipt_time": return 5000000; + case "network.upnp": return false; + case "network.natpmp": return false; + case "network.address_filter": return true; + case "network.restricted_nat_retries": return 3; + case "network.tls.certificate_path": return ""; + case "network.tls.private_key_path": return ""; + case "network.application.path": return "/app"; + case "network.application.https.enabled": return false; + case "network.application.https.listen_address": return ""; + case "network.application.http.enabled": return false; + case "network.application.http.listen_address": return ""; + case "network.protocol.udp.enabled": return false; + case "network.protocol.udp.socket_pool_size": return 0; + case "network.protocol.udp.listen_address": return ""; + case "network.protocol.udp.public_address": return ""; + case "network.protocol.tcp.connect": return false; + case "network.protocol.tcp.listen": return false; + case "network.protocol.tcp.max_connections": return 32; + case "network.protocol.tcp.listen_address": return ""; + case "network.protocol.tcp.public_address": return ""; + case "network.protocol.ws.connect": return true; + case "network.protocol.ws.listen": return false; + case "network.protocol.ws.max_connections": return 16; + case "network.protocol.ws.listen_address": return ""; + case "network.protocol.ws.path": return "/ws"; + case "network.protocol.ws.public_address": return ""; + case "network.protocol.wss.connect": return true; + case "network.protocol.wss.listen": return false; + case "network.protocol.wss.max_connections": return 16; + case "network.protocol.wss.listen_address": return ""; + case "network.protocol.wss.path": return "/ws"; + case "network.protocol.wss.public_address": return ""; + default: + console.log("config key '" + key +"' doesn't exist"); break; + } + }; + true + "#).expect("failed to eval"), JsValue::TRUE); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// + +#[wasm_bindgen_test] +fn test_construct() { + setup(); + + assert_eq!( + js_sys::eval( + r#" + let vc = new VeilidCore(); + true + "# + ) + .expect("failed to eval"), + JsValue::TRUE + ); +} + +#[wasm_bindgen_test(async)] +async fn test_startup_shutdown() { + setup(); + + assert_eq!( + JsFuture::from( + js_sys::eval( + r#" + (async function() { + let vc = new VeilidCore(); + await vc.startup(window.stateChangeCallback, window.configCallback); + await vc.shutdown(); + return true; + })().then(v => { + console.log("finished: " + v); + return v; + }); + "# + ) + .expect("failed to eval") + .dyn_into::() + .unwrap() + ) + .await, + Ok(JsValue::TRUE) + ); +} + +#[wasm_bindgen_test(async)] +async fn test_attach_detach() { + setup(); + + assert_eq!( + JsFuture::from( + js_sys::eval( + r#" + (async function() { + let vc = new VeilidCore(); + await vc.startup(window.stateChangeCallback, window.configCallback); + await vc.attach(); + await window.sleep(1000); + await vc.detach(); + await vc.shutdown(); + return true; + })().then(v => { + console.log("finished: " + v); + return v; + }); + "# + ) + .expect("failed to eval") + .dyn_into::() + .unwrap() + ) + .await, + Ok(JsValue::TRUE) + ); +}