From 59c14f3b227bdc9c0aabeedf305733d07f9c7ac4 Mon Sep 17 00:00:00 2001 From: John Smith Date: Wed, 7 Jun 2023 21:55:23 -0400 Subject: [PATCH] checkpoint --- Cargo.lock | 4 +- veilid-cli/Cargo.toml | 8 +- veilid-cli/src/client_api_connection.rs | 516 +++++++++++++----------- veilid-cli/src/command_processor.rs | 23 +- veilid-cli/src/main.rs | 2 +- veilid-cli/src/peers_table_view.rs | 1 - veilid-cli/src/ui.rs | 67 +-- veilid-tools/src/lib.rs | 5 +- 8 files changed, 340 insertions(+), 286 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22646141..f841b887 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6160,6 +6160,7 @@ dependencies = [ "cursive_table_view", "directories", "flexi_logger", + "flume", "futures", "hex", "json", @@ -6168,10 +6169,11 @@ dependencies = [ "serde", "serde_derive", "serial_test", + "stop-token", "thiserror", "tokio 1.28.2", "tokio-util", - "veilid-core", + "veilid-tools", ] [[package]] diff --git a/veilid-cli/Cargo.toml b/veilid-cli/Cargo.toml index cc58e0d3..021275d2 100644 --- a/veilid-cli/Cargo.toml +++ b/veilid-cli/Cargo.toml @@ -12,8 +12,8 @@ path = "src/main.rs" [features] default = [ "rt-tokio" ] macos = [ "cursive/ncurses-backend" ] -rt-async-std = [ "async-std", "veilid-core/rt-async-std", "cursive/rt-async-std" ] -rt-tokio = [ "tokio", "tokio-util", "veilid-core/rt-tokio", "cursive/rt-tokio" ] +rt-async-std = [ "async-std", "veilid-tools/rt-async-std", "cursive/rt-async-std" ] +rt-tokio = [ "tokio", "tokio-util", "veilid-tools/rt-tokio", "cursive/rt-tokio" ] [dependencies] async-std = { version = "^1.9", features = ["unstable", "attributes"], optional = true } @@ -41,8 +41,10 @@ flexi_logger = { version = "^0", features = ["use_chrono_for_offset"] } thiserror = "^1" crossbeam-channel = "^0" hex = "^0" -veilid-core = { path = "../veilid-core" } +veilid-tools = { path = "../veilid-tools", default-features = false } json = "^0" +stop-token = { version = "^0", default-features = false } +flume = { version = "^0", features = ["async"] } [dev-dependencies] serial_test = "^0" diff --git a/veilid-cli/src/client_api_connection.rs b/veilid-cli/src/client_api_connection.rs index bdabcb96..21d4ba5c 100644 --- a/veilid-cli/src/client_api_connection.rs +++ b/veilid-cli/src/client_api_connection.rs @@ -1,94 +1,66 @@ use crate::command_processor::*; use crate::tools::*; -use futures::future::FutureExt; use serde::de::DeserializeOwned; use std::cell::RefCell; use std::net::SocketAddr; use std::rc::Rc; -use veilid_core::tools::*; -use veilid_core::*; +use stop_token::{future::FutureExt as _, StopSource, StopToken}; -fn map_to_internal_error(e: T) -> VeilidAPIError { - VeilidAPIError::Internal { - message: e.to_string(), +use veilid_tools::*; +cfg_if! { + if #[cfg(feature="rt-async-std")] { + use async_std::io::prelude::BufReadExt; + use async_std::io::WriteExt; + use async_std::io::BufReader; + } else if #[cfg(feature="rt-tokio")] { + use tokio::io::AsyncBufReadExt; + use tokio::io::AsyncWriteExt; + use tokio::io::BufReader; } } -fn decode_api_result( - reader: &api_result::Reader, -) -> VeilidAPIResult { - match reader.which().map_err(map_to_internal_error)? { - api_result::Which::Ok(v) => { - let ok_val = v.map_err(map_to_internal_error)?; - let res: T = veilid_core::deserialize_json(ok_val).map_err(map_to_internal_error)?; - Ok(res) - } - api_result::Which::Err(e) => { - let err_val = e.map_err(map_to_internal_error)?; - let res: VeilidAPIError = - veilid_core::deserialize_json(err_val).map_err(map_to_internal_error)?; - Err(res) - } - } -} +// fn map_to_internal_error(e: T) -> VeilidAPIError { +// VeilidAPIError::Internal { +// message: e.to_string(), +// } +// } -struct VeilidClientImpl { - comproc: CommandProcessor, -} +// fn decode_api_result( +// reader: &api_result::Reader, +// ) -> VeilidAPIResult { +// match reader.which().map_err(map_to_internal_error)? { +// api_result::Which::Ok(v) => { +// let ok_val = v.map_err(map_to_internal_error)?; +// let res: T = veilid_core::deserialize_json(ok_val).map_err(map_to_internal_error)?; +// Ok(res) +// } +// api_result::Which::Err(e) => { +// let err_val = e.map_err(map_to_internal_error)?; +// let res: VeilidAPIError = +// veilid_core::deserialize_json(err_val).map_err(map_to_internal_error)?; +// Err(res) +// } +// } +// } -impl VeilidClientImpl { - pub fn new(comproc: CommandProcessor) -> Self { - Self { comproc } - } -} +// struct VeilidClientImpl { +// comproc: CommandProcessor, +// } -impl veilid_client::Server for VeilidClientImpl { - fn update( - &mut self, - params: veilid_client::UpdateParams, - _results: veilid_client::UpdateResults, - ) -> Promise<(), ::capnp::Error> { - let veilid_update = pry!(pry!(params.get()).get_veilid_update()); - let veilid_update: VeilidUpdate = pry_result!(deserialize_json(veilid_update)); +// impl VeilidClientImpl { +// pub fn new(comproc: CommandProcessor) -> Self { +// Self { comproc } +// } +// } - match veilid_update { - VeilidUpdate::Log(log) => { - self.comproc.update_log(log); - } - VeilidUpdate::AppMessage(msg) => { - self.comproc.update_app_message(msg); - } - VeilidUpdate::AppCall(call) => { - self.comproc.update_app_call(call); - } - VeilidUpdate::Attachment(attachment) => { - self.comproc.update_attachment(attachment); - } - VeilidUpdate::Network(network) => { - self.comproc.update_network_status(network); - } - VeilidUpdate::Config(config) => { - self.comproc.update_config(config); - } - VeilidUpdate::RouteChange(route) => { - self.comproc.update_route(route); - } - VeilidUpdate::Shutdown => self.comproc.update_shutdown(), - VeilidUpdate::ValueChange(value_change) => { - self.comproc.update_value_change(value_change); - } - } - - Promise::ok(()) - } -} +// } struct ClientApiConnectionInner { comproc: CommandProcessor, connect_addr: Option, - disconnector: Option>, - server: Option>>, + server: Option>, server_settings: Option, + disconnector: Option, disconnect_requested: bool, cancel_eventual: Eventual, } @@ -106,9 +78,9 @@ impl ClientApiConnection { inner: Rc::new(RefCell::new(ClientApiConnectionInner { comproc, connect_addr: None, - disconnector: None, server: None, server_settings: None, + disconnector: None, disconnect_requested: false, cancel_eventual: Eventual::new(), })), @@ -123,196 +95,272 @@ impl ClientApiConnection { eventual.resolve(); // don't need to await this } - async fn process_veilid_state<'a>( - &'a mut self, - veilid_state: VeilidState, - ) -> Result<(), String> { - let mut inner = self.inner.borrow_mut(); - inner.comproc.update_attachment(veilid_state.attachment); - inner.comproc.update_network_status(veilid_state.network); - inner.comproc.update_config(veilid_state.config); - Ok(()) - } + // async fn process_veilid_state<'a>( + // &'a mut self, + // veilid_state: VeilidState, + // ) -> Result<(), String> { + // let mut inner = self.inner.borrow_mut(); + // inner.comproc.update_attachment(veilid_state.attachment); + // inner.comproc.update_network_status(veilid_state.network); + // inner.comproc.update_config(veilid_state.config); + // Ok(()) + // } - async fn spawn_rpc_system( - &mut self, - connect_addr: SocketAddr, - mut rpc_system: RpcSystem, - ) -> Result<(), String> { - let mut request; - { - let mut inner = self.inner.borrow_mut(); - - // Get the bootstrap server connection object - inner.server = Some(Rc::new(RefCell::new( - rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server), - ))); - - // Store our disconnector future for later (must happen after bootstrap, contrary to documentation) - inner.disconnector = Some(rpc_system.get_disconnector()); - - // Get a client object to pass to the server for status update callbacks - let client = capnp_rpc::new_client(VeilidClientImpl::new(inner.comproc.clone())); - - // Register our client and get a registration object back - request = inner - .server - .as_ref() - .unwrap() - .borrow_mut() - .register_request(); - request.get().set_veilid_client(client); - - inner - .comproc - .set_connection_state(ConnectionState::Connected( - connect_addr, - std::time::SystemTime::now(), - )); - } - - let rpc_jh = spawn_local(rpc_system); - - let reg_res: Result = (async { - // Send the request and get the state object and the registration object - let response = request - .send() - .promise - .await - .map_err(|e| format!("failed to send register request: {}", e))?; - let response = response - .get() - .map_err(|e| format!("failed to get register response: {}", e))?; - - // Get the registration object, which drops our connection when it is dropped - let registration = response - .get_registration() - .map_err(|e| format!("failed to get registration object: {}", e))?; - - // Get the initial veilid state - let veilid_state = response - .get_state() - .map_err(|e| format!("failed to get initial veilid state: {}", e))?; - - // Set up our state for the first time - let veilid_state: VeilidState = deserialize_json(veilid_state) - .map_err(|e| format!("failed to get deserialize veilid state: {}", e))?; - self.process_veilid_state(veilid_state).await?; - - // Save server settings - let server_settings = response - .get_settings() - .map_err(|e| format!("failed to get initial veilid server settings: {}", e))? - .to_owned(); - self.inner.borrow_mut().server_settings = Some(server_settings.clone()); - - // Don't drop the registration, doing so will remove the client - // object mapping from the server which we need for the update backchannel - Ok(registration) - }) - .await; - - let _registration = match reg_res { - Ok(v) => v, - Err(e) => { - rpc_jh.abort().await; - return Err(e); - } + async fn process_update(&self, update: json::JsonValue) { + let comproc = self.inner.borrow().comproc.clone(); + let Some(kind) = update["kind"].as_str() else { + comproc.log_message(format!("missing update kind: {}", update)); + return; }; - - // Wait until rpc system completion or disconnect was requested - let res = rpc_jh.await; - res.map_err(|e| format!("client RPC system error: {}", e)) + match kind { + "Log" => { + comproc.update_log(update); + } + "AppMessage" => { + comproc.update_app_message(update); + } + "AppCall" => { + comproc.update_app_call(update); + } + "Attachment" => { + comproc.update_attachment(update); + } + "Network" => { + comproc.update_network_status(update); + } + "Config" => { + comproc.update_config(update); + } + "RouteChange" => { + comproc.update_route(update); + } + "Shutdown" => comproc.update_shutdown(), + "ValueChange" => { + comproc.update_value_change(update); + } + _ => { + comproc.log_message(format!("unknown update kind: {}", update)); + } + } } + // async fn spawn_rpc_system( + // &mut self, + // connect_addr: SocketAddr, + // mut rpc_system: RpcSystem, + // ) -> Result<(), String> { + // let mut request; + // { + // let mut inner = self.inner.borrow_mut(); + + // // Get the bootstrap server connection object + // inner.server = Some(Rc::new(RefCell::new( + // rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server), + // ))); + + // // Store our disconnector future for later (must happen after bootstrap, contrary to documentation) + // inner.disconnector = Some(rpc_system.get_disconnector()); + + // // Get a client object to pass to the server for status update callbacks + // let client = capnp_rpc::new_client(VeilidClientImpl::new(inner.comproc.clone())); + + // // Register our client and get a registration object back + // request = inner + // .server + // .as_ref() + // .unwrap() + // .borrow_mut() + // .register_request(); + // request.get().set_veilid_client(client); + + // inner + // .comproc + // .set_connection_state(ConnectionState::Connected( + // connect_addr, + // std::time::SystemTime::now(), + // )); + // } + + // let rpc_jh = spawn_local(rpc_system); + + // let reg_res: Result = (async { + // // Send the request and get the state object and the registration object + // let response = request + // .send() + // .promise + // .await + // .map_err(|e| format!("failed to send register request: {}", e))?; + // let response = response + // .get() + // .map_err(|e| format!("failed to get register response: {}", e))?; + + // // Get the registration object, which drops our connection when it is dropped + // let registration = response + // .get_registration() + // .map_err(|e| format!("failed to get registration object: {}", e))?; + + // // Get the initial veilid state + // let veilid_state = response + // .get_state() + // .map_err(|e| format!("failed to get initial veilid state: {}", e))?; + + // // Set up our state for the first time + // let veilid_state: VeilidState = deserialize_json(veilid_state) + // .map_err(|e| format!("failed to get deserialize veilid state: {}", e))?; + // self.process_veilid_state(veilid_state).await?; + + // // Save server settings + // let server_settings = response + // .get_settings() + // .map_err(|e| format!("failed to get initial veilid server settings: {}", e))? + // .to_owned(); + // self.inner.borrow_mut().server_settings = Some(server_settings.clone()); + + // // Don't drop the registration, doing so will remove the client + // // object mapping from the server which we need for the update backchannel + // Ok(registration) + // }) + // .await; + + // let _registration = match reg_res { + // Ok(v) => v, + // Err(e) => { + // rpc_jh.abort().await; + // return Err(e); + // } + // }; + + // // Wait until rpc system completion or disconnect was requested + // let res = rpc_jh.await; + // res.map_err(|e| format!("client RPC system error: {}", e)) + // } + async fn handle_connection(&mut self, connect_addr: SocketAddr) -> Result<(), String> { trace!("ClientApiConnection::handle_connection"); - self.inner.borrow_mut().connect_addr = Some(connect_addr); + let stop_token = { + let stop_source = StopSource::new(); + let token = stop_source.token(); + let mut inner = self.inner.borrow_mut(); + inner.connect_addr = Some(connect_addr); + inner.disconnector = Some(stop_source); + token + }; + // Connect the TCP socket let stream = TcpStream::connect(connect_addr) .await .map_err(map_to_string)?; + // If it succeed, disable nagle algorithm stream.set_nodelay(true).map_err(map_to_string)?; - // Create the VAT network + // Split the stream cfg_if! { if #[cfg(feature="rt-async-std")] { use futures::AsyncReadExt; let (reader, writer) = stream.split(); + let mut reader = BufReader::new(reader); } else if #[cfg(feature="rt-tokio")] { - pub use tokio_util::compat::*; let (reader, writer) = stream.into_split(); - let reader = reader.compat(); - let writer = writer.compat_write(); + let mut reader = BufReader::new(reader); } } - let rpc_network = Box::new(twoparty::VatNetwork::new( - reader, - writer, - rpc_twoparty_capnp::Side::Client, - Default::default(), - )); - - // Create the rpc system - let rpc_system = RpcSystem::new(rpc_network, None); - - // Process the rpc system until we decide we're done - match self.spawn_rpc_system(connect_addr, rpc_system).await { - Ok(()) => {} - Err(e) => { - error!("Failed to spawn client RPC system: {}", e); + // Process lines + let mut line = String::new(); + while let Ok(r) = reader + .read_line(&mut line) + .timeout_at(stop_token.clone()) + .await + { + match r { + Ok(size) => { + // Exit on EOF + if size == 0 { + // Disconnected + return Err("Connection closed".to_owned()); + } + } + Err(e) => { + // Disconnected + return Err("Connection lost".to_owned()); + } } - } - // Drop the server and disconnector too (if we still have it) - let mut inner = self.inner.borrow_mut(); - let disconnect_requested = inner.disconnect_requested; - inner.server_settings = None; - inner.server = None; - inner.disconnector = None; - inner.disconnect_requested = false; - inner.connect_addr = None; - - if !disconnect_requested { - // Connection lost - Err("Connection lost".to_owned()) - } else { - // Connection finished - Ok(()) - } - } - - pub fn cancellable(&mut self, p: Promise) -> Promise - where - T: 'static, - { - let (mut cancel_instance, cancel_eventual) = { - let inner = self.inner.borrow(); - ( - inner.cancel_eventual.instance_empty().fuse(), - inner.cancel_eventual.clone(), - ) - }; - let mut p = p.fuse(); - - Promise::from_future(async move { - let out = select! { - a = p => { - a - }, - _ = cancel_instance => { - Err(capnp::Error::failed("cancelled".into())) + // Unmarshal json + let j = match json::parse(line.trim()) { + Ok(v) => v, + Err(e) => { + error!("failed to parse server response: {}", e); + continue; } }; - drop(cancel_instance); - cancel_eventual.reset(); - out - }) + + if j["type"] == "Update" { + self.process_update(j).await; + } + } + + // Connection finished + Ok(()) + + // let rpc_network = Box::new(twoparty::VatNetwork::new( + // reader, + // writer, + // rpc_twoparty_capnp::Side::Client, + // Default::default(), + // )); + + // // Create the rpc system + // let rpc_system = RpcSystem::new(rpc_network, None); + + // // Process the rpc system until we decide we're done + // match self.spawn_rpc_system(connect_addr, rpc_system).await { + // Ok(()) => {} + // Err(e) => { + // error!("Failed to spawn client RPC system: {}", e); + // } + // } + + // // Drop the server and disconnector too (if we still have it) + // let mut inner = self.inner.borrow_mut(); + // let disconnect_requested = inner.disconnect_requested; + // inner.server_settings = None; + // inner.server = None; + // inner.disconnector = None; + // inner.disconnect_requested = false; + // inner.connect_addr = None; } + // pub fn cancellable(&mut self, p: Promise) -> Promise + // where + // T: 'static, + // { + // let (mut cancel_instance, cancel_eventual) = { + // let inner = self.inner.borrow(); + // ( + // inner.cancel_eventual.instance_empty().fuse(), + // inner.cancel_eventual.clone(), + // ) + // }; + // let mut p = p.fuse(); + + // Promise::from_future(async move { + // let out = select! { + // a = p => { + // a + // }, + // _ = cancel_instance => { + // Err(capnp::Error::failed("cancelled".into())) + // } + // }; + // drop(cancel_instance); + // cancel_eventual.reset(); + // out + // }) + // } + pub async fn server_attach(&mut self) -> Result<(), String> { trace!("ClientApiConnection::server_attach"); let server = { diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index 60fe90d7..f47ca014 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -5,8 +5,7 @@ use std::cell::*; use std::net::SocketAddr; use std::rc::Rc; use std::time::SystemTime; -use veilid_core::tools::*; -use veilid_core::*; +use veilid_tools::*; pub fn convert_loglevel(s: &str) -> Result { match s.to_ascii_lowercase().as_str() { @@ -387,7 +386,11 @@ reply - reply to an AppCall not handled directly by the server // calls into ui //////////////////////////////////////////// - pub fn update_attachment(&mut self, attachment: veilid_core::VeilidStateAttachment) { + pub fn log_message(&mut self, message: String) { + self.inner().ui.add_node_event(message); + } + + pub fn update_attachment(&mut self, attachment: json::JsonValue) { self.inner_mut().ui.set_attachment_state( attachment.state, attachment.public_internet_ready, @@ -424,17 +427,17 @@ reply - reply to an AppCall not handled directly by the server self.inner().ui.add_node_event(out); } } - pub fn update_value_change(&mut self, value_change: veilid_core::VeilidValueChange) { - let out = format!("Value change: {:?}", value_change); + pub fn update_value_change(&mut self, value_change: json::JsonValue) { + let out = format!("Value change: {:?}", value_change.as_str().unwrap_or("???")); self.inner().ui.add_node_event(out); } - pub fn update_log(&mut self, log: veilid_core::VeilidLog) { + pub fn update_log(&mut self, log: json::JsonValue) { self.inner().ui.add_node_event(format!( "{}: {}{}", - log.log_level, - log.message, - if let Some(bt) = log.backtrace { + log["log_level"].as_str().unwrap_or("???"), + log["message"].as_str().unwrap_or("???"), + if let Some(bt) = log["backtrace"].as_str() { format!("\nBacktrace:\n{}", bt) } else { "".to_owned() @@ -442,7 +445,7 @@ reply - reply to an AppCall not handled directly by the server )); } - pub fn update_app_message(&mut self, msg: veilid_core::VeilidAppMessage) { + pub fn update_app_message(&mut self, msg: json::JsonValue) { // check is message body is ascii printable let mut printable = true; for c in msg.message() { diff --git a/veilid-cli/src/main.rs b/veilid-cli/src/main.rs index 73ac351d..517a02e2 100644 --- a/veilid-cli/src/main.rs +++ b/veilid-cli/src/main.rs @@ -3,7 +3,7 @@ #![recursion_limit = "256"] use crate::tools::*; -use veilid_core::tools::*; +use veilid_tools::*; use clap::{Arg, ColorChoice, Command}; use flexi_logger::*; diff --git a/veilid-cli/src/peers_table_view.rs b/veilid-cli/src/peers_table_view.rs index ff328476..e51aaf62 100644 --- a/veilid-cli/src/peers_table_view.rs +++ b/veilid-cli/src/peers_table_view.rs @@ -1,7 +1,6 @@ use super::*; use cursive_table_view::*; use std::cmp::Ordering; -use veilid_core::*; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum PeerTableColumn { diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index 48b38db9..9a7c8ed3 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -12,12 +12,11 @@ use cursive::Cursive; use cursive::CursiveRunnable; use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView}; //use cursive_multiplex::*; -use log::*; use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::rc::Rc; use thiserror::Error; -use veilid_core::*; +use veilid_tools::*; ////////////////////////////////////////////////////////////// /// @@ -50,20 +49,20 @@ impl Dirty { pub type UICallback = Box; struct UIState { - attachment_state: Dirty, + attachment_state: Dirty, public_internet_ready: Dirty, local_network_ready: Dirty, network_started: Dirty, network_down_up: Dirty<(f32, f32)>, connection_state: Dirty, - peers_state: Dirty>, + peers_state: Dirty>, node_id: Dirty, } impl UIState { pub fn new() -> Self { Self { - attachment_state: Dirty::new(AttachmentState::Detached), + attachment_state: Dirty::new("Detached".to_owned()), public_internet_ready: Dirty::new(false), local_network_ready: Dirty::new(false), network_started: Dirty::new(false), @@ -239,15 +238,16 @@ impl UI { s.find_name("peers").unwrap() } fn render_attachment_state(inner: &mut UIInner) -> String { - let att = match inner.ui_state.attachment_state.get() { - AttachmentState::Detached => "[----]", - AttachmentState::Attaching => "[/ ]", - AttachmentState::AttachedWeak => "[| ]", - AttachmentState::AttachedGood => "[|| ]", - AttachmentState::AttachedStrong => "[||| ]", - AttachmentState::FullyAttached => "[||||]", - AttachmentState::OverAttached => "[++++]", - AttachmentState::Detaching => "[////]", + let att = match inner.ui_state.attachment_state.get().as_str() { + "Detached" => "[----]", + "Attaching" => "[/ ]", + "AttachedWeak" => "[| ]", + "AttachedGood" => "[|| ]", + "AttachedStrong" => "[||| ]", + "FullyAttached" => "[||||]", + "OverAttached" => "[++++]", + "Detaching" => "[////]", + _ => "[????]", }; let pi = if *inner.ui_state.public_internet_ready.get() { "+P" @@ -272,15 +272,16 @@ impl UI { } fn render_button_attach<'a>(inner: &mut UIInner) -> (&'a str, bool) { if let ConnectionState::Connected(_, _) = inner.ui_state.connection_state.get() { - match inner.ui_state.attachment_state.get() { - AttachmentState::Detached => ("Attach", true), - AttachmentState::Attaching => ("Detach", true), - AttachmentState::AttachedWeak => ("Detach", true), - AttachmentState::AttachedGood => ("Detach", true), - AttachmentState::AttachedStrong => ("Detach", true), - AttachmentState::FullyAttached => ("Detach", true), - AttachmentState::OverAttached => ("Detach", true), - AttachmentState::Detaching => ("Detach", false), + match inner.ui_state.attachment_state.get().as_str() { + "Detached" => ("Attach", true), + "Attaching" => ("Detach", true), + "AttachedWeak" => ("Detach", true), + "AttachedGood" => ("Detach", true), + "AttachedStrong" => ("Detach", true), + "FullyAttached" => ("Detach", true), + "OverAttached" => ("Detach", true), + "Detaching" => ("Detach", false), + _ => ("???", false), } } else { (" ---- ", false) @@ -412,15 +413,17 @@ impl UI { } fn on_button_attach_pressed(s: &mut Cursive) { - let action: Option = match Self::inner_mut(s).ui_state.attachment_state.get() { - AttachmentState::Detached => Some(true), - AttachmentState::Attaching => Some(false), - AttachmentState::AttachedWeak => Some(false), - AttachmentState::AttachedGood => Some(false), - AttachmentState::AttachedStrong => Some(false), - AttachmentState::FullyAttached => Some(false), - AttachmentState::OverAttached => Some(false), - AttachmentState::Detaching => None, + let action: Option = match Self::inner_mut(s).ui_state.attachment_state.get().as_str() + { + "Detached" => Some(true), + "Attaching" => Some(false), + "AttachedWeak" => Some(false), + "AttachedGood" => Some(false), + "AttachedStrong" => Some(false), + "FullyAttached" => Some(false), + "OverAttached" => Some(false), + "Detaching" => None, + _ => None, }; let mut cmdproc = Self::command_processor(s); if let Some(a) = action { diff --git a/veilid-tools/src/lib.rs b/veilid-tools/src/lib.rs index ed8e90c8..788e143d 100644 --- a/veilid-tools/src/lib.rs +++ b/veilid-tools/src/lib.rs @@ -140,8 +140,5 @@ pub use wasm::*; pub mod tests; // For iOS tests - #[no_mangle] -pub extern "C" fn main_rs() { - // start game code here -} +pub extern "C" fn main_rs() {}