checkpoint

This commit is contained in:
John Smith 2023-06-07 21:55:23 -04:00
parent 317f036598
commit 59c14f3b22
8 changed files with 340 additions and 286 deletions

4
Cargo.lock generated
View File

@ -6160,6 +6160,7 @@ dependencies = [
"cursive_table_view", "cursive_table_view",
"directories", "directories",
"flexi_logger", "flexi_logger",
"flume",
"futures", "futures",
"hex", "hex",
"json", "json",
@ -6168,10 +6169,11 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"serial_test", "serial_test",
"stop-token",
"thiserror", "thiserror",
"tokio 1.28.2", "tokio 1.28.2",
"tokio-util", "tokio-util",
"veilid-core", "veilid-tools",
] ]
[[package]] [[package]]

View File

@ -12,8 +12,8 @@ path = "src/main.rs"
[features] [features]
default = [ "rt-tokio" ] default = [ "rt-tokio" ]
macos = [ "cursive/ncurses-backend" ] macos = [ "cursive/ncurses-backend" ]
rt-async-std = [ "async-std", "veilid-core/rt-async-std", "cursive/rt-async-std" ] rt-async-std = [ "async-std", "veilid-tools/rt-async-std", "cursive/rt-async-std" ]
rt-tokio = [ "tokio", "tokio-util", "veilid-core/rt-tokio", "cursive/rt-tokio" ] rt-tokio = [ "tokio", "tokio-util", "veilid-tools/rt-tokio", "cursive/rt-tokio" ]
[dependencies] [dependencies]
async-std = { version = "^1.9", features = ["unstable", "attributes"], optional = true } 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" thiserror = "^1"
crossbeam-channel = "^0" crossbeam-channel = "^0"
hex = "^0" hex = "^0"
veilid-core = { path = "../veilid-core" } veilid-tools = { path = "../veilid-tools", default-features = false }
json = "^0" json = "^0"
stop-token = { version = "^0", default-features = false }
flume = { version = "^0", features = ["async"] }
[dev-dependencies] [dev-dependencies]
serial_test = "^0" serial_test = "^0"

View File

@ -1,94 +1,66 @@
use crate::command_processor::*; use crate::command_processor::*;
use crate::tools::*; use crate::tools::*;
use futures::future::FutureExt;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::cell::RefCell; use std::cell::RefCell;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::rc::Rc; use std::rc::Rc;
use veilid_core::tools::*; use stop_token::{future::FutureExt as _, StopSource, StopToken};
use veilid_core::*;
fn map_to_internal_error<T: ToString>(e: T) -> VeilidAPIError { use veilid_tools::*;
VeilidAPIError::Internal { cfg_if! {
message: e.to_string(), 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<T: DeserializeOwned + fmt::Debug>( // fn map_to_internal_error<T: ToString>(e: T) -> VeilidAPIError {
reader: &api_result::Reader, // VeilidAPIError::Internal {
) -> VeilidAPIResult<T> { // message: e.to_string(),
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)
}
}
}
struct VeilidClientImpl { // fn decode_api_result<T: DeserializeOwned + fmt::Debug>(
comproc: CommandProcessor, // reader: &api_result::Reader,
} // ) -> VeilidAPIResult<T> {
// 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 { // struct VeilidClientImpl {
pub fn new(comproc: CommandProcessor) -> Self { // comproc: CommandProcessor,
Self { comproc } // }
}
}
impl veilid_client::Server for VeilidClientImpl { // impl VeilidClientImpl {
fn update( // pub fn new(comproc: CommandProcessor) -> Self {
&mut self, // Self { comproc }
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));
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 { struct ClientApiConnectionInner {
comproc: CommandProcessor, comproc: CommandProcessor,
connect_addr: Option<SocketAddr>, connect_addr: Option<SocketAddr>,
disconnector: Option<Disconnector<rpc_twoparty_capnp::Side>>, server: Option<flume::Sender<String>>,
server: Option<Rc<RefCell<veilid_server::Client>>>,
server_settings: Option<String>, server_settings: Option<String>,
disconnector: Option<StopSource>,
disconnect_requested: bool, disconnect_requested: bool,
cancel_eventual: Eventual, cancel_eventual: Eventual,
} }
@ -106,9 +78,9 @@ impl ClientApiConnection {
inner: Rc::new(RefCell::new(ClientApiConnectionInner { inner: Rc::new(RefCell::new(ClientApiConnectionInner {
comproc, comproc,
connect_addr: None, connect_addr: None,
disconnector: None,
server: None, server: None,
server_settings: None, server_settings: None,
disconnector: None,
disconnect_requested: false, disconnect_requested: false,
cancel_eventual: Eventual::new(), cancel_eventual: Eventual::new(),
})), })),
@ -123,195 +95,271 @@ impl ClientApiConnection {
eventual.resolve(); // don't need to await this eventual.resolve(); // don't need to await this
} }
async fn process_veilid_state<'a>( // async fn process_veilid_state<'a>(
&'a mut self, // &'a mut self,
veilid_state: VeilidState, // veilid_state: VeilidState,
) -> Result<(), String> { // ) -> Result<(), String> {
let mut inner = self.inner.borrow_mut(); // let mut inner = self.inner.borrow_mut();
inner.comproc.update_attachment(veilid_state.attachment); // inner.comproc.update_attachment(veilid_state.attachment);
inner.comproc.update_network_status(veilid_state.network); // inner.comproc.update_network_status(veilid_state.network);
inner.comproc.update_config(veilid_state.config); // inner.comproc.update_config(veilid_state.config);
Ok(()) // Ok(())
} // }
async fn spawn_rpc_system( async fn process_update(&self, update: json::JsonValue) {
&mut self, let comproc = self.inner.borrow().comproc.clone();
connect_addr: SocketAddr, let Some(kind) = update["kind"].as_str() else {
mut rpc_system: RpcSystem<rpc_twoparty_capnp::Side>, comproc.log_message(format!("missing update kind: {}", update));
) -> Result<(), String> { return;
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<registration::Client, String> = (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);
}
}; };
match kind {
// Wait until rpc system completion or disconnect was requested "Log" => {
let res = rpc_jh.await; comproc.update_log(update);
res.map_err(|e| format!("client RPC system error: {}", e))
} }
"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<rpc_twoparty_capnp::Side>,
// ) -> 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<registration::Client, String> = (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> { async fn handle_connection(&mut self, connect_addr: SocketAddr) -> Result<(), String> {
trace!("ClientApiConnection::handle_connection"); 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 // Connect the TCP socket
let stream = TcpStream::connect(connect_addr) let stream = TcpStream::connect(connect_addr)
.await .await
.map_err(map_to_string)?; .map_err(map_to_string)?;
// If it succeed, disable nagle algorithm // If it succeed, disable nagle algorithm
stream.set_nodelay(true).map_err(map_to_string)?; stream.set_nodelay(true).map_err(map_to_string)?;
// Create the VAT network // Split the stream
cfg_if! { cfg_if! {
if #[cfg(feature="rt-async-std")] { if #[cfg(feature="rt-async-std")] {
use futures::AsyncReadExt; use futures::AsyncReadExt;
let (reader, writer) = stream.split(); let (reader, writer) = stream.split();
let mut reader = BufReader::new(reader);
} else if #[cfg(feature="rt-tokio")] { } else if #[cfg(feature="rt-tokio")] {
pub use tokio_util::compat::*;
let (reader, writer) = stream.into_split(); let (reader, writer) = stream.into_split();
let reader = reader.compat(); let mut reader = BufReader::new(reader);
let writer = writer.compat_write();
} }
} }
let rpc_network = Box::new(twoparty::VatNetwork::new( // Process lines
reader, let mut line = String::new();
writer, while let Ok(r) = reader
rpc_twoparty_capnp::Side::Client, .read_line(&mut line)
Default::default(), .timeout_at(stop_token.clone())
)); .await
{
// Create the rpc system match r {
let rpc_system = RpcSystem::new(rpc_network, None); Ok(size) => {
// Exit on EOF
// Process the rpc system until we decide we're done if size == 0 {
match self.spawn_rpc_system(connect_addr, rpc_system).await { // Disconnected
Ok(()) => {} return Err("Connection closed".to_owned());
}
}
Err(e) => { Err(e) => {
error!("Failed to spawn client RPC system: {}", e); // Disconnected
return Err("Connection lost".to_owned());
} }
} }
// Drop the server and disconnector too (if we still have it) // Unmarshal json
let mut inner = self.inner.borrow_mut(); let j = match json::parse(line.trim()) {
let disconnect_requested = inner.disconnect_requested; Ok(v) => v,
inner.server_settings = None; Err(e) => {
inner.server = None; error!("failed to parse server response: {}", e);
inner.disconnector = None; continue;
inner.disconnect_requested = false; }
inner.connect_addr = None; };
if j["type"] == "Update" {
self.process_update(j).await;
}
}
if !disconnect_requested {
// Connection lost
Err("Connection lost".to_owned())
} else {
// Connection finished // Connection finished
Ok(()) 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<T>(&mut self, p: Promise<T, capnp::Error>) -> Promise<T, capnp::Error> // pub fn cancellable<T>(&mut self, p: Promise<T, capnp::Error>) -> Promise<T, capnp::Error>
where // where
T: 'static, // T: 'static,
{ // {
let (mut cancel_instance, cancel_eventual) = { // let (mut cancel_instance, cancel_eventual) = {
let inner = self.inner.borrow(); // let inner = self.inner.borrow();
( // (
inner.cancel_eventual.instance_empty().fuse(), // inner.cancel_eventual.instance_empty().fuse(),
inner.cancel_eventual.clone(), // inner.cancel_eventual.clone(),
) // )
}; // };
let mut p = p.fuse(); // let mut p = p.fuse();
Promise::from_future(async move { // Promise::from_future(async move {
let out = select! { // let out = select! {
a = p => { // a = p => {
a // a
}, // },
_ = cancel_instance => { // _ = cancel_instance => {
Err(capnp::Error::failed("cancelled".into())) // Err(capnp::Error::failed("cancelled".into()))
} // }
}; // };
drop(cancel_instance); // drop(cancel_instance);
cancel_eventual.reset(); // cancel_eventual.reset();
out // out
}) // })
} // }
pub async fn server_attach(&mut self) -> Result<(), String> { pub async fn server_attach(&mut self) -> Result<(), String> {
trace!("ClientApiConnection::server_attach"); trace!("ClientApiConnection::server_attach");

View File

@ -5,8 +5,7 @@ use std::cell::*;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::rc::Rc; use std::rc::Rc;
use std::time::SystemTime; use std::time::SystemTime;
use veilid_core::tools::*; use veilid_tools::*;
use veilid_core::*;
pub fn convert_loglevel(s: &str) -> Result<VeilidConfigLogLevel, String> { pub fn convert_loglevel(s: &str) -> Result<VeilidConfigLogLevel, String> {
match s.to_ascii_lowercase().as_str() { 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 // 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( self.inner_mut().ui.set_attachment_state(
attachment.state, attachment.state,
attachment.public_internet_ready, 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); self.inner().ui.add_node_event(out);
} }
} }
pub fn update_value_change(&mut self, value_change: veilid_core::VeilidValueChange) { pub fn update_value_change(&mut self, value_change: json::JsonValue) {
let out = format!("Value change: {:?}", value_change); let out = format!("Value change: {:?}", value_change.as_str().unwrap_or("???"));
self.inner().ui.add_node_event(out); 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!( self.inner().ui.add_node_event(format!(
"{}: {}{}", "{}: {}{}",
log.log_level, log["log_level"].as_str().unwrap_or("???"),
log.message, log["message"].as_str().unwrap_or("???"),
if let Some(bt) = log.backtrace { if let Some(bt) = log["backtrace"].as_str() {
format!("\nBacktrace:\n{}", bt) format!("\nBacktrace:\n{}", bt)
} else { } else {
"".to_owned() "".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 // check is message body is ascii printable
let mut printable = true; let mut printable = true;
for c in msg.message() { for c in msg.message() {

View File

@ -3,7 +3,7 @@
#![recursion_limit = "256"] #![recursion_limit = "256"]
use crate::tools::*; use crate::tools::*;
use veilid_core::tools::*; use veilid_tools::*;
use clap::{Arg, ColorChoice, Command}; use clap::{Arg, ColorChoice, Command};
use flexi_logger::*; use flexi_logger::*;

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
use cursive_table_view::*; use cursive_table_view::*;
use std::cmp::Ordering; use std::cmp::Ordering;
use veilid_core::*;
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum PeerTableColumn { pub enum PeerTableColumn {

View File

@ -12,12 +12,11 @@ use cursive::Cursive;
use cursive::CursiveRunnable; use cursive::CursiveRunnable;
use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView}; use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView};
//use cursive_multiplex::*; //use cursive_multiplex::*;
use log::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::rc::Rc; use std::rc::Rc;
use thiserror::Error; use thiserror::Error;
use veilid_core::*; use veilid_tools::*;
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
/// ///
@ -50,20 +49,20 @@ impl<T> Dirty<T> {
pub type UICallback = Box<dyn Fn(&mut Cursive) + Send>; pub type UICallback = Box<dyn Fn(&mut Cursive) + Send>;
struct UIState { struct UIState {
attachment_state: Dirty<AttachmentState>, attachment_state: Dirty<String>,
public_internet_ready: Dirty<bool>, public_internet_ready: Dirty<bool>,
local_network_ready: Dirty<bool>, local_network_ready: Dirty<bool>,
network_started: Dirty<bool>, network_started: Dirty<bool>,
network_down_up: Dirty<(f32, f32)>, network_down_up: Dirty<(f32, f32)>,
connection_state: Dirty<ConnectionState>, connection_state: Dirty<ConnectionState>,
peers_state: Dirty<Vec<PeerTableData>>, peers_state: Dirty<Vec<json::JsonValue>>,
node_id: Dirty<String>, node_id: Dirty<String>,
} }
impl UIState { impl UIState {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
attachment_state: Dirty::new(AttachmentState::Detached), attachment_state: Dirty::new("Detached".to_owned()),
public_internet_ready: Dirty::new(false), public_internet_ready: Dirty::new(false),
local_network_ready: Dirty::new(false), local_network_ready: Dirty::new(false),
network_started: Dirty::new(false), network_started: Dirty::new(false),
@ -239,15 +238,16 @@ impl UI {
s.find_name("peers").unwrap() s.find_name("peers").unwrap()
} }
fn render_attachment_state(inner: &mut UIInner) -> String { fn render_attachment_state(inner: &mut UIInner) -> String {
let att = match inner.ui_state.attachment_state.get() { let att = match inner.ui_state.attachment_state.get().as_str() {
AttachmentState::Detached => "[----]", "Detached" => "[----]",
AttachmentState::Attaching => "[/ ]", "Attaching" => "[/ ]",
AttachmentState::AttachedWeak => "[| ]", "AttachedWeak" => "[| ]",
AttachmentState::AttachedGood => "[|| ]", "AttachedGood" => "[|| ]",
AttachmentState::AttachedStrong => "[||| ]", "AttachedStrong" => "[||| ]",
AttachmentState::FullyAttached => "[||||]", "FullyAttached" => "[||||]",
AttachmentState::OverAttached => "[++++]", "OverAttached" => "[++++]",
AttachmentState::Detaching => "[////]", "Detaching" => "[////]",
_ => "[????]",
}; };
let pi = if *inner.ui_state.public_internet_ready.get() { let pi = if *inner.ui_state.public_internet_ready.get() {
"+P" "+P"
@ -272,15 +272,16 @@ impl UI {
} }
fn render_button_attach<'a>(inner: &mut UIInner) -> (&'a str, bool) { fn render_button_attach<'a>(inner: &mut UIInner) -> (&'a str, bool) {
if let ConnectionState::Connected(_, _) = inner.ui_state.connection_state.get() { if let ConnectionState::Connected(_, _) = inner.ui_state.connection_state.get() {
match inner.ui_state.attachment_state.get() { match inner.ui_state.attachment_state.get().as_str() {
AttachmentState::Detached => ("Attach", true), "Detached" => ("Attach", true),
AttachmentState::Attaching => ("Detach", true), "Attaching" => ("Detach", true),
AttachmentState::AttachedWeak => ("Detach", true), "AttachedWeak" => ("Detach", true),
AttachmentState::AttachedGood => ("Detach", true), "AttachedGood" => ("Detach", true),
AttachmentState::AttachedStrong => ("Detach", true), "AttachedStrong" => ("Detach", true),
AttachmentState::FullyAttached => ("Detach", true), "FullyAttached" => ("Detach", true),
AttachmentState::OverAttached => ("Detach", true), "OverAttached" => ("Detach", true),
AttachmentState::Detaching => ("Detach", false), "Detaching" => ("Detach", false),
_ => ("???", false),
} }
} else { } else {
(" ---- ", false) (" ---- ", false)
@ -412,15 +413,17 @@ impl UI {
} }
fn on_button_attach_pressed(s: &mut Cursive) { fn on_button_attach_pressed(s: &mut Cursive) {
let action: Option<bool> = match Self::inner_mut(s).ui_state.attachment_state.get() { let action: Option<bool> = match Self::inner_mut(s).ui_state.attachment_state.get().as_str()
AttachmentState::Detached => Some(true), {
AttachmentState::Attaching => Some(false), "Detached" => Some(true),
AttachmentState::AttachedWeak => Some(false), "Attaching" => Some(false),
AttachmentState::AttachedGood => Some(false), "AttachedWeak" => Some(false),
AttachmentState::AttachedStrong => Some(false), "AttachedGood" => Some(false),
AttachmentState::FullyAttached => Some(false), "AttachedStrong" => Some(false),
AttachmentState::OverAttached => Some(false), "FullyAttached" => Some(false),
AttachmentState::Detaching => None, "OverAttached" => Some(false),
"Detaching" => None,
_ => None,
}; };
let mut cmdproc = Self::command_processor(s); let mut cmdproc = Self::command_processor(s);
if let Some(a) = action { if let Some(a) = action {

View File

@ -140,8 +140,5 @@ pub use wasm::*;
pub mod tests; pub mod tests;
// For iOS tests // For iOS tests
#[no_mangle] #[no_mangle]
pub extern "C" fn main_rs() { pub extern "C" fn main_rs() {}
// start game code here
}