mirror of
https://gitlab.com/veilid/veilid.git
synced 2024-10-01 01:26:08 -04:00
add interactive mode to veilid-cli
This commit is contained in:
parent
931d145719
commit
e98877fc71
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -1255,6 +1255,23 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"futures-core",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_winapi"
|
name = "crossterm_winapi"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@ -1314,7 +1331,7 @@ dependencies = [
|
|||||||
"async-std",
|
"async-std",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"crossterm",
|
"crossterm 0.25.0",
|
||||||
"cursive_core",
|
"cursive_core",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
@ -4324,6 +4341,22 @@ version = "1.0.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustyline-async"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b6eb06391513b2184f0a5405c11a4a0a5302e8be442f4c5c35267187c2b37d5"
|
||||||
|
dependencies = [
|
||||||
|
"crossterm 0.27.0",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project",
|
||||||
|
"thingbuf",
|
||||||
|
"thiserror",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
@ -4917,6 +4950,16 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thingbuf"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4706f1bfb859af03f099ada2de3cea3e515843c2d3e93b7893f16d94a37f9415"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot 0.12.1",
|
||||||
|
"pin-project",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.56"
|
version = "1.0.56"
|
||||||
@ -5641,6 +5684,7 @@ dependencies = [
|
|||||||
"lru",
|
"lru",
|
||||||
"owning_ref",
|
"owning_ref",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
|
"rustyline-async",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
@ -12,6 +12,7 @@ resolver = "2"
|
|||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
cursive = { git = "https://gitlab.com/veilid/cursive.git" }
|
cursive = { git = "https://gitlab.com/veilid/cursive.git" }
|
||||||
cursive_core = { git = "https://gitlab.com/veilid/cursive.git" }
|
cursive_core = { git = "https://gitlab.com/veilid/cursive.git" }
|
||||||
|
nom = { git = "https://gitlab.com/emixa-d/ansi-parser.git" }
|
||||||
|
|
||||||
# For local development
|
# For local development
|
||||||
# keyvaluedb = { path = "../keyvaluedb/keyvaluedb" }
|
# keyvaluedb = { path = "../keyvaluedb/keyvaluedb" }
|
||||||
|
@ -55,7 +55,7 @@ flexi_logger = { version = "^0", features = ["use_chrono_for_offset"] }
|
|||||||
thiserror = "^1"
|
thiserror = "^1"
|
||||||
crossbeam-channel = "^0"
|
crossbeam-channel = "^0"
|
||||||
hex = "^0"
|
hex = "^0"
|
||||||
veilid-tools = { version = "0.2.5", path = "../veilid-tools", default-features = false}
|
veilid-tools = { version = "0.2.5", path = "../veilid-tools", default-features = false }
|
||||||
|
|
||||||
json = "^0"
|
json = "^0"
|
||||||
stop-token = { version = "^0", default-features = false }
|
stop-token = { version = "^0", default-features = false }
|
||||||
@ -67,6 +67,7 @@ chrono = "0.4.31"
|
|||||||
owning_ref = "0.4.1"
|
owning_ref = "0.4.1"
|
||||||
unicode-width = "0.1.11"
|
unicode-width = "0.1.11"
|
||||||
lru = "0.10.1"
|
lru = "0.10.1"
|
||||||
|
rustyline-async = "0.4.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "^2"
|
serial_test = "^2"
|
||||||
|
@ -80,7 +80,7 @@ impl ClientApiConnection {
|
|||||||
async fn process_veilid_update(&self, update: json::JsonValue) {
|
async fn process_veilid_update(&self, update: json::JsonValue) {
|
||||||
let comproc = self.inner.lock().comproc.clone();
|
let comproc = self.inner.lock().comproc.clone();
|
||||||
let Some(kind) = update["kind"].as_str() else {
|
let Some(kind) = update["kind"].as_str() else {
|
||||||
comproc.log_message(Level::Error, format!("missing update kind: {}", update));
|
comproc.log_message(Level::Error, &format!("missing update kind: {}", update));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match kind {
|
match kind {
|
||||||
@ -110,7 +110,7 @@ impl ClientApiConnection {
|
|||||||
comproc.update_value_change(&update);
|
comproc.update_value_change(&update);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
comproc.log_message(Level::Error, format!("unknown update kind: {}", update));
|
comproc.log_message(Level::Error, &format!("unknown update kind: {}", update));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ impl ConnectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CommandProcessorInner {
|
struct CommandProcessorInner {
|
||||||
ui_sender: UISender,
|
ui_sender: Box<dyn UISender>,
|
||||||
capi: Option<ClientApiConnection>,
|
capi: Option<ClientApiConnection>,
|
||||||
reconnect: bool,
|
reconnect: bool,
|
||||||
finished: bool,
|
finished: bool,
|
||||||
@ -60,7 +60,7 @@ pub struct CommandProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CommandProcessor {
|
impl CommandProcessor {
|
||||||
pub fn new(ui_sender: UISender, settings: &Settings) -> Self {
|
pub fn new(ui_sender: Box<dyn UISender>, settings: &Settings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Mutex::new(CommandProcessorInner {
|
inner: Arc::new(Mutex::new(CommandProcessorInner {
|
||||||
ui_sender,
|
ui_sender,
|
||||||
@ -86,8 +86,8 @@ impl CommandProcessor {
|
|||||||
fn inner_mut(&self) -> MutexGuard<CommandProcessorInner> {
|
fn inner_mut(&self) -> MutexGuard<CommandProcessorInner> {
|
||||||
self.inner.lock()
|
self.inner.lock()
|
||||||
}
|
}
|
||||||
fn ui_sender(&self) -> UISender {
|
fn ui_sender(&self) -> Box<dyn UISender> {
|
||||||
self.inner.lock().ui_sender.clone()
|
self.inner.lock().ui_sender.clone_uisender()
|
||||||
}
|
}
|
||||||
fn capi(&self) -> ClientApiConnection {
|
fn capi(&self) -> ClientApiConnection {
|
||||||
self.inner.lock().capi.as_ref().unwrap().clone()
|
self.inner.lock().capi.as_ref().unwrap().clone()
|
||||||
@ -126,7 +126,7 @@ impl CommandProcessor {
|
|||||||
|
|
||||||
ui.add_node_event(
|
ui.add_node_event(
|
||||||
Level::Info,
|
Level::Info,
|
||||||
format!(
|
&format!(
|
||||||
r#"Client Commands:
|
r#"Client Commands:
|
||||||
exit/quit exit the client
|
exit/quit exit the client
|
||||||
disconnect disconnect the client from the Veilid node
|
disconnect disconnect the client from the Veilid node
|
||||||
@ -190,11 +190,11 @@ Server Debug Commands:
|
|||||||
spawn_detached_local(async move {
|
spawn_detached_local(async move {
|
||||||
match capi.server_debug(command_line).await {
|
match capi.server_debug(command_line).await {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
ui.add_node_event(Level::Info, output);
|
ui.add_node_event(Level::Info, &output);
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ui.add_node_event(Level::Error, e.to_string());
|
ui.add_node_event(Level::Error, &e);
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ Server Debug Commands:
|
|||||||
let log_level = match convert_loglevel(&rest.unwrap_or_default()) {
|
let log_level = match convert_loglevel(&rest.unwrap_or_default()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ui.add_node_event(Level::Error, format!("Failed to change log level: {}", e));
|
ui.add_node_event(Level::Error, &format!("Failed to change log level: {}", e));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ Server Debug Commands:
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
ui.display_string_dialog(
|
ui.display_string_dialog(
|
||||||
"Server command 'change_log_level' failed",
|
"Server command 'change_log_level' failed",
|
||||||
e.to_string(),
|
&e,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -247,11 +247,11 @@ Server Debug Commands:
|
|||||||
match flag.as_str() {
|
match flag.as_str() {
|
||||||
"app_messages" => {
|
"app_messages" => {
|
||||||
this.inner.lock().enable_app_messages = true;
|
this.inner.lock().enable_app_messages = true;
|
||||||
ui.add_node_event(Level::Info, format!("flag enabled: {}", flag));
|
ui.add_node_event(Level::Info, &format!("flag enabled: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
ui.add_node_event(Level::Error, format!("unknown flag: {}", flag));
|
ui.add_node_event(Level::Error, &format!("unknown flag: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,11 +269,11 @@ Server Debug Commands:
|
|||||||
match flag.as_str() {
|
match flag.as_str() {
|
||||||
"app_messages" => {
|
"app_messages" => {
|
||||||
this.inner.lock().enable_app_messages = false;
|
this.inner.lock().enable_app_messages = false;
|
||||||
ui.add_node_event(Level::Info, format!("flag disabled: {}", flag));
|
ui.add_node_event(Level::Info, &format!("flag disabled: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
ui.add_node_event(Level::Error, format!("unknown flag: {}", flag));
|
ui.add_node_event(Level::Error, &format!("unknown flag: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,13 +413,13 @@ Server Debug Commands:
|
|||||||
// calls into ui
|
// calls into ui
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
|
|
||||||
pub fn log_message(&self, log_level: Level, message: String) {
|
pub fn log_message(&self, log_level: Level, message: &str) {
|
||||||
self.inner().ui_sender.add_node_event(log_level, message);
|
self.inner().ui_sender.add_node_event(log_level, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attachment(&self, attachment: &json::JsonValue) {
|
pub fn update_attachment(&self, attachment: &json::JsonValue) {
|
||||||
self.inner_mut().ui_sender.set_attachment_state(
|
self.inner_mut().ui_sender.set_attachment_state(
|
||||||
attachment["state"].as_str().unwrap_or_default().to_owned(),
|
attachment["state"].as_str().unwrap_or_default(),
|
||||||
attachment["public_internet_ready"]
|
attachment["public_internet_ready"]
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
@ -458,7 +458,7 @@ Server Debug Commands:
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if !out.is_empty() {
|
if !out.is_empty() {
|
||||||
self.inner().ui_sender.add_node_event(Level::Info, out);
|
self.inner().ui_sender.add_node_event(Level::Info, &out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update_value_change(&self, value_change: &json::JsonValue) {
|
pub fn update_value_change(&self, value_change: &json::JsonValue) {
|
||||||
@ -475,7 +475,7 @@ Server Debug Commands:
|
|||||||
datastr,
|
datastr,
|
||||||
if truncated { "..." } else { "" }
|
if truncated { "..." } else { "" }
|
||||||
);
|
);
|
||||||
self.inner().ui_sender.add_node_event(Level::Info, out);
|
self.inner().ui_sender.add_node_event(Level::Info, &out);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_log(&self, log: &json::JsonValue) {
|
pub fn update_log(&self, log: &json::JsonValue) {
|
||||||
@ -483,7 +483,7 @@ Server Debug Commands:
|
|||||||
Level::from_str(log["log_level"].as_str().unwrap_or("error")).unwrap_or(Level::Error);
|
Level::from_str(log["log_level"].as_str().unwrap_or("error")).unwrap_or(Level::Error);
|
||||||
self.inner().ui_sender.add_node_event(
|
self.inner().ui_sender.add_node_event(
|
||||||
log_level,
|
log_level,
|
||||||
format!(
|
&format!(
|
||||||
"{}: {}{}",
|
"{}: {}{}",
|
||||||
log["log_level"].as_str().unwrap_or("???"),
|
log["log_level"].as_str().unwrap_or("???"),
|
||||||
log["message"].as_str().unwrap_or("???"),
|
log["message"].as_str().unwrap_or("???"),
|
||||||
@ -530,7 +530,7 @@ Server Debug Commands:
|
|||||||
|
|
||||||
self.inner().ui_sender.add_node_event(
|
self.inner().ui_sender.add_node_event(
|
||||||
Level::Info,
|
Level::Info,
|
||||||
format!(
|
&format!(
|
||||||
"AppMessage ({:?}): {}{}",
|
"AppMessage ({:?}): {}{}",
|
||||||
msg["sender"],
|
msg["sender"],
|
||||||
strmsg,
|
strmsg,
|
||||||
@ -570,7 +570,7 @@ Server Debug Commands:
|
|||||||
|
|
||||||
self.inner().ui_sender.add_node_event(
|
self.inner().ui_sender.add_node_event(
|
||||||
Level::Info,
|
Level::Info,
|
||||||
format!(
|
&format!(
|
||||||
"AppCall ({:?}) id = {:016x} : {}{}",
|
"AppCall ({:?}) id = {:016x} : {}{}",
|
||||||
call["sender"],
|
call["sender"],
|
||||||
id,
|
id,
|
||||||
|
1430
veilid-cli/src/cursive_ui.rs
Normal file
1430
veilid-cli/src/cursive_ui.rs
Normal file
File diff suppressed because it is too large
Load Diff
189
veilid-cli/src/interactive_ui.rs
Normal file
189
veilid-cli/src/interactive_ui.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::command_processor::*;
|
||||||
|
use crate::settings::*;
|
||||||
|
use crate::tools::*;
|
||||||
|
use crate::ui::*;
|
||||||
|
|
||||||
|
use flexi_logger::writers::LogWriter;
|
||||||
|
use rustyline_async::SharedWriter;
|
||||||
|
use rustyline_async::{Readline, ReadlineError, ReadlineEvent};
|
||||||
|
|
||||||
|
pub type InteractiveUICallback = Box<dyn FnMut() + Send>;
|
||||||
|
|
||||||
|
pub struct InteractiveUIInner {
|
||||||
|
cmdproc: Option<CommandProcessor>,
|
||||||
|
stdout: Option<SharedWriter>,
|
||||||
|
error: Option<String>,
|
||||||
|
done: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InteractiveUI {
|
||||||
|
inner: Arc<Mutex<InteractiveUIInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractiveUI {
|
||||||
|
pub fn new(_settings: &Settings) -> (Self, InteractiveUISender) {
|
||||||
|
// Create the UI object
|
||||||
|
let this = Self {
|
||||||
|
inner: Arc::new(Mutex::new(InteractiveUIInner {
|
||||||
|
cmdproc: None,
|
||||||
|
stdout: None,
|
||||||
|
error: None,
|
||||||
|
done: false,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ui_sender = InteractiveUISender {
|
||||||
|
inner: this.inner.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(this, ui_sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn command_loop(&self) {
|
||||||
|
let (mut readline, mut stdout) =
|
||||||
|
match Readline::new("> ".to_owned()).map_err(|e| e.to_string()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.inner.lock().stdout = Some(stdout.clone());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if self.inner.lock().done {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Some(e) = self.inner.lock().error.clone() {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
match readline.readline().await {
|
||||||
|
Ok(ReadlineEvent::Line(line)) => {
|
||||||
|
let line = line.trim();
|
||||||
|
if line == "clear" {
|
||||||
|
if let Err(e) = readline.clear() {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
}
|
||||||
|
} else if !line.is_empty() {
|
||||||
|
readline.add_history_entry(line.to_string());
|
||||||
|
let cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = &cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
line,
|
||||||
|
UICallback::Interactive(Box::new({
|
||||||
|
//let mut stdout = stdout.clone();
|
||||||
|
move || {
|
||||||
|
// if let Err(e) = writeln!(stdout) {
|
||||||
|
// println!("Error: {:?}", e);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
) {
|
||||||
|
if let Err(e) = writeln!(stdout, "Error: {}", e) {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ReadlineEvent::Interrupted) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(ReadlineEvent::Eof) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(ReadlineError::Closed) => {}
|
||||||
|
Err(ReadlineError::IO(e)) => {
|
||||||
|
println!("IO Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = readline.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UI for InteractiveUI {
|
||||||
|
fn set_command_processor(&mut self, cmdproc: CommandProcessor) {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.cmdproc = Some(cmdproc);
|
||||||
|
}
|
||||||
|
// Note: Cursive is not re-entrant, can't borrow_mut self.siv again after this
|
||||||
|
fn run_async(&mut self) -> Pin<Box<dyn core::future::Future<Output = ()>>> {
|
||||||
|
let this = self.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
this.command_loop().await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InteractiveUISender {
|
||||||
|
inner: Arc<Mutex<InteractiveUIInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UISender for InteractiveUISender {
|
||||||
|
fn clone_uisender(&self) -> Box<dyn UISender> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
fn as_logwriter(&self) -> Option<Box<dyn LogWriter>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_string_dialog(&self, title: &str, text: &str, close_cb: UICallback) {
|
||||||
|
println!("{}: {}", title, text);
|
||||||
|
if let UICallback::Interactive(mut close_cb) = close_cb {
|
||||||
|
close_cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit(&self) {
|
||||||
|
self.inner.lock().done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_callback(&self, callback: UICallback) {
|
||||||
|
if let UICallback::Interactive(mut callback) = callback {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_attachment_state(
|
||||||
|
&mut self,
|
||||||
|
_state: &str,
|
||||||
|
_public_internet_ready: bool,
|
||||||
|
_local_network_ready: bool,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_network_status(
|
||||||
|
&mut self,
|
||||||
|
_started: bool,
|
||||||
|
_bps_down: u64,
|
||||||
|
_bps_up: u64,
|
||||||
|
mut _peers: Vec<json::JsonValue>,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_config(&mut self, _config: &json::JsonValue) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_connection_state(&mut self, _state: ConnectionState) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_node_event(&self, _log_color: Level, event: &str) {
|
||||||
|
let Some(mut stdout) = self.inner.lock().stdout.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Err(e) = writeln!(stdout, "{}", event) {
|
||||||
|
self.inner.lock().error = Some(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
use crate::{settings::NamedSocketAddrs, tools::*};
|
use crate::{settings::NamedSocketAddrs, tools::*, ui::*};
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use flexi_logger::*;
|
use flexi_logger::*;
|
||||||
@ -11,6 +11,8 @@ use std::path::PathBuf;
|
|||||||
mod cached_text_view;
|
mod cached_text_view;
|
||||||
mod client_api_connection;
|
mod client_api_connection;
|
||||||
mod command_processor;
|
mod command_processor;
|
||||||
|
mod cursive_ui;
|
||||||
|
mod interactive_ui;
|
||||||
mod peers_table_view;
|
mod peers_table_view;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod tools;
|
mod tools;
|
||||||
@ -31,7 +33,7 @@ struct CmdlineArgs {
|
|||||||
#[arg(long, short = 'p')]
|
#[arg(long, short = 'p')]
|
||||||
ipc_path: Option<PathBuf>,
|
ipc_path: Option<PathBuf>,
|
||||||
/// Subnode index to use when connecting
|
/// Subnode index to use when connecting
|
||||||
#[arg(long, short = 'i', default_value = "0")]
|
#[arg(long, default_value = "0")]
|
||||||
subnode_index: usize,
|
subnode_index: usize,
|
||||||
/// Address to connect to
|
/// Address to connect to
|
||||||
#[arg(long, short = 'a')]
|
#[arg(long, short = 'a')]
|
||||||
@ -45,6 +47,23 @@ struct CmdlineArgs {
|
|||||||
/// log level
|
/// log level
|
||||||
#[arg(value_enum)]
|
#[arg(value_enum)]
|
||||||
log_level: Option<LogLevel>,
|
log_level: Option<LogLevel>,
|
||||||
|
/// interactive
|
||||||
|
#[arg(long, short = 'i', group = "execution_mode")]
|
||||||
|
interactive: bool,
|
||||||
|
/// evaluate
|
||||||
|
#[arg(long, short = 'e', group = "execution_mode")]
|
||||||
|
evaluate: Option<String>,
|
||||||
|
/// show log
|
||||||
|
#[arg(long, short = 'l', group = "execution_mode")]
|
||||||
|
show_log: bool,
|
||||||
|
/// read commands from file
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
short = 'f',
|
||||||
|
group = "execution_mode",
|
||||||
|
value_name = "COMMAND_FILE"
|
||||||
|
)]
|
||||||
|
command_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
@ -78,8 +97,29 @@ fn main() -> Result<(), String> {
|
|||||||
settings.logging.terminal.enabled = true;
|
settings.logging.terminal.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are running in interactive mode disable some things
|
||||||
|
let mut enable_cursive = true;
|
||||||
|
if args.interactive || args.show_log || args.command_file.is_some() || args.evaluate.is_some() {
|
||||||
|
settings.logging.terminal.enabled = false;
|
||||||
|
enable_cursive = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Create UI object
|
// Create UI object
|
||||||
let (mut sivui, uisender) = ui::UI::new(settings.interface.node_log.scrollback, &settings);
|
let (mut ui, uisender) = if enable_cursive {
|
||||||
|
let (ui, uisender) = cursive_ui::CursiveUI::new(&settings);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else if args.interactive {
|
||||||
|
let (ui, uisender) = interactive_ui::InteractiveUI::new(&settings);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
panic!("unknown ui mode");
|
||||||
|
};
|
||||||
|
|
||||||
// Set up loggers
|
// Set up loggers
|
||||||
{
|
{
|
||||||
@ -105,13 +145,13 @@ fn main() -> Result<(), String> {
|
|||||||
FileSpec::default()
|
FileSpec::default()
|
||||||
.directory(settings.logging.file.directory.clone())
|
.directory(settings.logging.file.directory.clone())
|
||||||
.suppress_timestamp(),
|
.suppress_timestamp(),
|
||||||
Box::new(uisender.clone()),
|
uisender.as_logwriter().unwrap(),
|
||||||
)
|
)
|
||||||
.start()
|
.start()
|
||||||
.expect("failed to initialize logger!");
|
.expect("failed to initialize logger!");
|
||||||
} else {
|
} else {
|
||||||
logger
|
logger
|
||||||
.log_to_writer(Box::new(uisender.clone()))
|
.log_to_writer(uisender.as_logwriter().unwrap())
|
||||||
.start()
|
.start()
|
||||||
.expect("failed to initialize logger!");
|
.expect("failed to initialize logger!");
|
||||||
}
|
}
|
||||||
@ -195,7 +235,8 @@ fn main() -> Result<(), String> {
|
|||||||
// Create command processor
|
// Create command processor
|
||||||
debug!("Creating Command Processor ");
|
debug!("Creating Command Processor ");
|
||||||
let comproc = command_processor::CommandProcessor::new(uisender, &settings);
|
let comproc = command_processor::CommandProcessor::new(uisender, &settings);
|
||||||
sivui.set_command_processor(comproc.clone());
|
|
||||||
|
ui.set_command_processor(comproc.clone());
|
||||||
|
|
||||||
// Create client api client side
|
// Create client api client side
|
||||||
info!("Starting API connection");
|
info!("Starting API connection");
|
||||||
@ -221,7 +262,7 @@ fn main() -> Result<(), String> {
|
|||||||
block_on(async move {
|
block_on(async move {
|
||||||
// Start UI
|
// Start UI
|
||||||
let ui_future = async move {
|
let ui_future = async move {
|
||||||
sivui.run_async().await;
|
ui.run_async().await;
|
||||||
|
|
||||||
// When UI quits, close connection and command processor cleanly
|
// When UI quits, close connection and command processor cleanly
|
||||||
comproc2.quit();
|
comproc2.quit();
|
||||||
|
1398
veilid-cli/src/ui.rs
1398
veilid-cli/src/ui.rs
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user