diff --git a/veilid-flutter/rust/Cargo.lock b/veilid-flutter/rust/Cargo.lock index c17b4259..b08a2afd 100644 --- a/veilid-flutter/rust/Cargo.lock +++ b/veilid-flutter/rust/Cargo.lock @@ -109,6 +109,9 @@ name = "anyhow" version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" +dependencies = [ + "backtrace", +] [[package]] name = "arrayref" @@ -1357,9 +1360,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.112" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" [[package]] name = "libsqlite3-sys" @@ -2392,9 +2395,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +checksum = "0f82496b90c36d70af5fcd482edaa2e0bd16fade569de1330405fecbbdac736b" dependencies = [ "libc", "winapi", @@ -2432,9 +2435,9 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "syn" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", @@ -2718,10 +2721,12 @@ dependencies = [ name = "veilid-flutter" version = "0.1.0" dependencies = [ + "anyhow", "async-std", "cfg-if 1.0.0", "flutter_rust_bridge", "log", + "parking_lot", "veilid-core", ] diff --git a/veilid-flutter/rust/Cargo.toml b/veilid-flutter/rust/Cargo.toml index aaa0d04d..144b202e 100644 --- a/veilid-flutter/rust/Cargo.toml +++ b/veilid-flutter/rust/Cargo.toml @@ -10,7 +10,9 @@ crate-type = ["cdylib", "staticlib"] 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"] } [build-dependencies] cfg-if = "^1" diff --git a/veilid-flutter/rust/build.rs b/veilid-flutter/rust/build.rs index fd607976..41a8567e 100644 --- a/veilid-flutter/rust/build.rs +++ b/veilid-flutter/rust/build.rs @@ -83,5 +83,5 @@ fn main() { .wait() .expect("flutter_rust_bridge_codegen was not running"); - println!("cargo:rerun-if-changed=src/api.c"); + println!("cargo:rerun-if-changed={}", input_path.to_str().unwrap()); } diff --git a/veilid-flutter/rust/src/api.rs b/veilid-flutter/rust/src/api.rs index f2dfe774..27873c49 100644 --- a/veilid-flutter/rust/src/api.rs +++ b/veilid-flutter/rust/src/api.rs @@ -2,6 +2,13 @@ use std::sync::Arc; use flutter_rust_bridge::*; use log::*; use std::collections::HashMap; +use async_std::sync::Mutex as AsyncMutex; +use anyhow::*; +use std::fmt; + +// Globals + +static API: AsyncMutex> = AsyncMutex::new(None); ///////////////////////////////////////// // Config Settings @@ -100,45 +107,45 @@ pub struct VeilidConfigNetwork { pub leases: VeilidConfigLeases, } -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct VeilidConfigTableStore { pub directory: String, pub delete: bool, } -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct VeilidConfigBlockStore { pub directory: String, pub delete: bool, } -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct VeilidConfigProtectedStore { - pub allow_insecure_fallback: bool, - pub always_use_insecure_storage: bool, - pub insecure_fallback_directory: String, - pub delete: bool, + } -#[derive(Default, Clone)] -pub struct VeilidConfigCapabilities { - pub protocol_udp: bool, - pub protocol_connect_tcp: bool, - pub protocol_accept_tcp: bool, - pub protocol_connect_ws: bool, - pub protocol_accept_ws: bool, - pub protocol_connect_wss: bool, - pub protocol_accept_wss: bool, -} - -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct VeilidConfig { pub program_name: String, pub namespace: String, - pub capabilities: VeilidConfigCapabilities, - pub protected_store: VeilidConfigProtectedStore, + // 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: VeilidConfigTableStore, + // Block Store pub block_store: VeilidConfigBlockStore, + // Network pub network: VeilidConfigNetwork, } @@ -173,6 +180,45 @@ pub enum VeilidAPIError { }, } +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 { @@ -233,7 +279,7 @@ impl VeilidUpdate { #[derive(Debug)] pub struct VeilidState { - attachment: AttachmentState, + pub attachment: AttachmentState, } impl VeilidState { @@ -244,142 +290,156 @@ impl VeilidState { } } -type Result = std::result::Result; - ///////////////////////////////////////// pub fn startup_veilid_core(sink: StreamSink, config: VeilidConfig) -> Result { - let core = veilid_core::VeilidCore::new(); - - // convert config to hashmap - let config_map = HashMap::>::new(); - macro_rules! get_config { - ($key:expr) => { - config_map.insert(stringify!($key)[7..].to_owned(), Box::new($key.clone())); - } - } - macro_rules! default_config { - ($key:expr, $default_value:expr) => { - config_map.insert(stringify!($key)[7..].to_owned(), Box::new($default_value)); - } - } - get_config!(config.program_name); - get_config!(config.namespace); - get_config!(config.capabilities.protocol_udp); - get_config!(config.capabilities.protocol_connect_tcp); - get_config!(config.capabilities.protocol_accept_tcp); - get_config!(config.capabilities.protocol_connect_ws); - get_config!(config.capabilities.protocol_accept_ws); - get_config!(config.capabilities.protocol_connect_wss); - get_config!(config.capabilities.protocol_accept_wss); - get_config!(config.table_store.directory); - get_config!(config.table_store.delete); - get_config!(config.block_store.directory); - get_config!(config.block_store.delete); - get_config!(config.protected_store.allow_insecure_fallback); - get_config!(config.protected_store.always_use_insecure_storage); - get_config!(config.protected_store.insecure_fallback_directory); - get_config!(config.protected_store.delete); - get_config!(config.network.node_id); - get_config!(config.network.node_id_secret); - get_config!(config.network.max_connections); - get_config!(config.network.connection_initial_timeout); - get_config!(config.network.bootstrap); - get_config!(config.network.dht.resolve_node_timeout); - get_config!(config.network.dht.resolve_node_count); - get_config!(config.network.dht.resolve_node_fanout); - get_config!(config.network.dht.max_find_node_count); - get_config!(config.network.dht.get_value_timeout); - get_config!(config.network.dht.get_value_count); - get_config!(config.network.dht.get_value_fanout); - get_config!(config.network.dht.set_value_timeout); - get_config!(config.network.dht.set_value_count); - get_config!(config.network.dht.set_value_fanout); - get_config!(config.network.dht.min_peer_count); - get_config!(config.network.dht.min_peer_refresh_time); - get_config!(config.network.dht.validate_dial_info_receipt_time); - get_config!(config.network.rpc.concurrency); - get_config!(config.network.rpc.queue_size); - get_config!(config.network.rpc.max_timestamp_behind); - get_config!(config.network.rpc.max_timestamp_ahead); - get_config!(config.network.rpc.timeout); - get_config!(config.network.rpc.max_route_hop_count); - get_config!(config.network.upnp); - get_config!(config.network.natpmp); - get_config!(config.network.enable_local_peer_scope); - get_config!(config.network.restricted_nat_retries); - default_config!(config.network.tls.certificate_path, ""); - default_config!(config.network.tls.private_key_path, ""); - default_config!(config.network.tls.connection_initial_timeout, 0u64); - default_config!(config.network.application.https.enabled, false); - default_config!(config.network.application.https.listen_address, ""); - default_config!(config.network.application.https.path, ""); - default_config!(config.network.application.https.url, Option::::None); - default_config!(config.network.application.http.enabled, false); - default_config!(config.network.application.http.listen_address, ""); - default_config!(config.network.application.http.path, ""); - default_config!(config.network.application.http.url, Option::::None); - get_config!(config.network.protocol.udp.enabled); - get_config!(config.network.protocol.udp.socket_pool_size); - get_config!(config.network.protocol.udp.listen_address); - get_config!(config.network.protocol.udp.public_address); - get_config!(config.network.protocol.tcp.connect); - get_config!(config.network.protocol.tcp.listen); - get_config!(config.network.protocol.tcp.max_connections); - get_config!(config.network.protocol.tcp.listen_address); - get_config!(config.network.protocol.tcp.public_address); - get_config!(config.network.protocol.ws.connect); - get_config!(config.network.protocol.ws.listen); - get_config!(config.network.protocol.ws.max_connections); - get_config!(config.network.protocol.ws.listen_address); - get_config!(config.network.protocol.ws.path); - get_config!(config.network.protocol.ws.url); - get_config!(config.network.protocol.wss.connect); - default_config!(config.network.protocol.wss.listen, false); - get_config!(config.network.protocol.wss.max_connections); - default_config!(config.network.protocol.wss.listen_address, ""); - default_config!(config.network.protocol.wss.path, ""); - default_config!(config.network.protocol.wss.url, Option::::None); - get_config!(config.network.leases.max_server_signal_leases); - get_config!(config.network.leases.max_server_relay_leases); - get_config!(config.network.leases.max_client_signal_leases); - get_config!(config.network.leases.max_client_relay_leases); - - let setup = veilid_core::VeilidCoreSetup { - update_callback: Arc::new( - move |update: veilid_core::VeilidUpdate| -> veilid_core::SystemPinBoxFuture<()> { - Box::pin(async move { - if !sink.add(VeilidUpdate::from_core(update)) { - error!("error sending veilid update callback"); - } - }) - }, - ), - config_callback: Arc::new( - move |key| { - config_map.get(&key).ok_or_else(|| { - let err = format!("config key '{}' doesn't exist", key); - error!("{}",err); - err - }).map(|v| { - *v.clone() - }) - } - ), - }; - async_std::task::block_on( async { - let api = core.startup(setup).await.map_err(|e| VeilidAPIError::InvalidConfig(e.clone()))?; - let core_state = api.get_state().await.map_err(VeilidAPIError::from_core)?; + + let api = API.lock().await; + if api.is_some() { + return Err(anyhow!(VeilidAPIError::AlreadyInitialized)); + } + + let core = veilid_core::VeilidCore::new(); + + // convert config to hashmap + let config_map = HashMap::>::new(); + macro_rules! get_config { + ($key:expr) => { + config_map.insert(stringify!($key)[7..].to_owned(), Box::new($key.clone())); + } + } + macro_rules! default_config { + ($key:expr, $default_value:expr) => { + config_map.insert(stringify!($key)[7..].to_owned(), Box::new($default_value)); + } + } + get_config!(config.program_name); + get_config!(config.namespace); + get_config!(config.capabilities.protocol_udp); + get_config!(config.capabilities.protocol_connect_tcp); + get_config!(config.capabilities.protocol_accept_tcp); + get_config!(config.capabilities.protocol_connect_ws); + get_config!(config.capabilities.protocol_accept_ws); + get_config!(config.capabilities.protocol_connect_wss); + get_config!(config.capabilities.protocol_accept_wss); + get_config!(config.table_store.directory); + get_config!(config.table_store.delete); + get_config!(config.block_store.directory); + get_config!(config.block_store.delete); + get_config!(config.protected_store.allow_insecure_fallback); + get_config!(config.protected_store.always_use_insecure_storage); + get_config!(config.protected_store.insecure_fallback_directory); + get_config!(config.protected_store.delete); + get_config!(config.network.node_id); + get_config!(config.network.node_id_secret); + get_config!(config.network.max_connections); + get_config!(config.network.connection_initial_timeout); + get_config!(config.network.bootstrap); + get_config!(config.network.dht.resolve_node_timeout); + get_config!(config.network.dht.resolve_node_count); + get_config!(config.network.dht.resolve_node_fanout); + get_config!(config.network.dht.max_find_node_count); + get_config!(config.network.dht.get_value_timeout); + get_config!(config.network.dht.get_value_count); + get_config!(config.network.dht.get_value_fanout); + get_config!(config.network.dht.set_value_timeout); + get_config!(config.network.dht.set_value_count); + get_config!(config.network.dht.set_value_fanout); + get_config!(config.network.dht.min_peer_count); + get_config!(config.network.dht.min_peer_refresh_time); + get_config!(config.network.dht.validate_dial_info_receipt_time); + get_config!(config.network.rpc.concurrency); + get_config!(config.network.rpc.queue_size); + get_config!(config.network.rpc.max_timestamp_behind); + get_config!(config.network.rpc.max_timestamp_ahead); + get_config!(config.network.rpc.timeout); + get_config!(config.network.rpc.max_route_hop_count); + get_config!(config.network.upnp); + get_config!(config.network.natpmp); + get_config!(config.network.enable_local_peer_scope); + get_config!(config.network.restricted_nat_retries); + default_config!(config.network.tls.certificate_path, ""); + default_config!(config.network.tls.private_key_path, ""); + default_config!(config.network.tls.connection_initial_timeout, 0u64); + default_config!(config.network.application.https.enabled, false); + default_config!(config.network.application.https.listen_address, ""); + default_config!(config.network.application.https.path, ""); + default_config!(config.network.application.https.url, Option::::None); + default_config!(config.network.application.http.enabled, false); + default_config!(config.network.application.http.listen_address, ""); + default_config!(config.network.application.http.path, ""); + default_config!(config.network.application.http.url, Option::::None); + get_config!(config.network.protocol.udp.enabled); + get_config!(config.network.protocol.udp.socket_pool_size); + get_config!(config.network.protocol.udp.listen_address); + get_config!(config.network.protocol.udp.public_address); + get_config!(config.network.protocol.tcp.connect); + get_config!(config.network.protocol.tcp.listen); + get_config!(config.network.protocol.tcp.max_connections); + get_config!(config.network.protocol.tcp.listen_address); + get_config!(config.network.protocol.tcp.public_address); + get_config!(config.network.protocol.ws.connect); + get_config!(config.network.protocol.ws.listen); + get_config!(config.network.protocol.ws.max_connections); + get_config!(config.network.protocol.ws.listen_address); + get_config!(config.network.protocol.ws.path); + get_config!(config.network.protocol.ws.url); + get_config!(config.network.protocol.wss.connect); + default_config!(config.network.protocol.wss.listen, false); + get_config!(config.network.protocol.wss.max_connections); + default_config!(config.network.protocol.wss.listen_address, ""); + default_config!(config.network.protocol.wss.path, ""); + default_config!(config.network.protocol.wss.url, Option::::None); + get_config!(config.network.leases.max_server_signal_leases); + get_config!(config.network.leases.max_server_relay_leases); + get_config!(config.network.leases.max_client_signal_leases); + get_config!(config.network.leases.max_client_relay_leases); + + let setup = veilid_core::VeilidCoreSetup { + update_callback: Arc::new( + move |update: veilid_core::VeilidUpdate| -> veilid_core::SystemPinBoxFuture<()> { + Box::pin(async move { + if !sink.add(VeilidUpdate::from_core(update)) { + error!("error sending veilid update callback"); + } + }) + }, + ), + config_callback: Arc::new( + move |key| { + config_map.get(&key).ok_or_else(|| { + let err = format!("config key '{}' doesn't exist", key); + error!("{}",err); + err + }).map(|v| { + *v.clone() + }) + } + ), + }; + + let veilid_api = core.startup(setup).await.map_err(|e| VeilidAPIError::InvalidConfig(e.clone()))?; + *api = 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 = API.lock().await.ok_or(anyhow!(VeilidAPIError::NotInitialized))?; + let core_state = veilid_api.get_state().await.map_err(VeilidAPIError::from_core)?; + Ok(VeilidState::from_core(core_state)) + }) } // xxx api functions pub fn shutdown_veilid_core() -> Result<()> { - + async_std::task::block_on( async { + let veilid_api = API.lock().await.take().ok_or(anyhow!(VeilidAPIError::NotInitialized))?; + veilid_api.shutdown().await; + Ok(()) + }) }