diff --git a/lazy-supplements/Cargo.toml b/lazy-supplements/Cargo.toml index 4eb0b1f..be91ed6 100644 --- a/lazy-supplements/Cargo.toml +++ b/lazy-supplements/Cargo.toml @@ -18,9 +18,11 @@ clap = { version = "4.5.38", features = ["derive"] } dirs = "6.0.0" futures = "0.3.31" libp2p.workspace = true +rustyline = "16.0.0" sea-orm = { version = "1.1.11", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "with-chrono", "with-uuid"] } sea-orm-migration.workspace = true serde = { version = "1.0.219", features = ["derive"] } +shell-words = "1.1.0" tempfile = { version = "3.20.0", optional = true } thiserror = "2.0.12" tokio = { version = "1.45.0", features = ["macros", "rt"] } diff --git a/lazy-supplements/src/cli/console.rs b/lazy-supplements/src/cli/console.rs index 7631d1f..c826729 100644 --- a/lazy-supplements/src/cli/console.rs +++ b/lazy-supplements/src/cli/console.rs @@ -1,6 +1,6 @@ -use std::time::Duration; +use std::{collections::HashMap, ffi::OsString, hash::Hash, time::Duration}; -use clap::Args; +use clap::{Args, Parser}; use futures::StreamExt; use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm}; use tokio::time::sleep; @@ -8,7 +8,55 @@ use tracing_subscriber::EnvFilter; use crate::{error::Error, global::GLOBAL}; -use super::ConfigArgs; +use super::{node::parse_and_run_console_node_command, ConfigArgs}; + +pub trait Executable { + fn execute(self) -> Result<(), Error>; +} + +pub trait ConsoleCommand { + fn execute_line(&self, line: String) -> Result<(), Error>; +} +pub struct ConsoleCommands { + content: HashMap<&'static str, Box) -> Result<(), Error>>>, +} +impl ConsoleCommands { + pub fn new() -> Self { + Self{content: HashMap::new()} + } + pub fn insert(&mut self,name: &'static str, f: Box) -> Result<(), Error>>) { + if let Some(_) = self.content.insert(name, f){ + unreachable!(); + }; + } + pub fn parse_line(&self, line: String) -> Result<(), Error>{ + let args = shell_words::split(&line)?; + if let Some(command_name) = args.first().map(|s| {s.clone()}) { + if let Some(command) = self.content.get(command_name.as_str()) { + command(args) + } else { + println!("Invalid command: {command_name}"); + self.print_commands(); + Ok(()) + } + } else { + Ok(()) + } + } + pub fn print_commands(&self) { + for key in self.content.keys(){ + println!("{key}"); + } + } +} + +impl Default for ConsoleCommands { + fn default() -> Self { + let mut commands = Self::new(); + commands.insert("node", Box::new(parse_and_run_console_node_command)); + commands + } +} #[derive(Args, Debug)] pub struct ConsoleArgs { @@ -17,12 +65,19 @@ pub struct ConsoleArgs { } impl ConsoleArgs { - pub async fn start_console(self) -> Result<(), Error>{ + pub async fn start_console(self, commands: ConsoleCommands) -> Result<(), Error> + { let _ = crate::global::GLOBAL.get_or_init_node_config(self.config.try_into_node_config().await?).await; tokio::spawn( async { GLOBAL.launch_swarm().await }); - sleep(Duration::from_secs(1)).await; - Ok(()) + let mut rl = rustyline::DefaultEditor::new()?; + loop { + match rl.readline(">> ") { + Ok(line) => commands.parse_line(line)?, + Err(x) => Err(x)?, + }; + } } -} \ No newline at end of file +} + diff --git a/lazy-supplements/src/cli/mod.rs b/lazy-supplements/src/cli/mod.rs index 0884ed0..d4f0846 100644 --- a/lazy-supplements/src/cli/mod.rs +++ b/lazy-supplements/src/cli/mod.rs @@ -7,7 +7,7 @@ mod node; mod server; pub use config::ConfigArgs; -pub use console::ConsoleArgs; +pub use console::{ConsoleArgs, ConsoleCommands}; pub use init::InitArgs; -pub use node::{ NodeArgs, NodeCommand, JoinNodeArgs }; +pub use node::{ NodeArgs, NodeCommand, JoinNodeArgs , ConsoleNodeArgs}; pub use server::ServerArgs; \ No newline at end of file diff --git a/lazy-supplements/src/cli/node.rs b/lazy-supplements/src/cli/node.rs index 2f17414..a985d9e 100644 --- a/lazy-supplements/src/cli/node.rs +++ b/lazy-supplements/src/cli/node.rs @@ -1,6 +1,6 @@ use std::{net::IpAddr, path::PathBuf}; -use clap::{Args, Subcommand}; +use clap::{Args, Parser, Subcommand}; use futures::StreamExt; use libp2p::{ multiaddr::Protocol, noise, ping, swarm::SwarmEvent, tcp, yamux, Multiaddr @@ -11,12 +11,30 @@ use crate::{cli::ServerArgs, error::Error}; use super::ConfigArgs; -#[derive(Args, Debug)] +#[derive(Debug, Args)] pub struct NodeArgs { #[command(subcommand)] pub command: NodeCommand } +#[derive(Debug, Parser)] +pub struct ConsoleNodeArgs { + #[command(flatten)] + pub args: NodeArgs, +} + +impl ConsoleNodeArgs { + pub fn run(self) -> Result<(), Error> { + println!("{self:?}"); + Ok(()) + } +} + +pub fn parse_and_run_console_node_command(s:Vec) -> Result<(), Error> { + let args = ConsoleNodeArgs::parse_from(s); + args.run() +} + #[derive(Args, Debug)] pub struct JoinNodeArgs { #[arg(long)] diff --git a/lazy-supplements/src/error.rs b/lazy-supplements/src/error.rs index 376b3e4..0be2735 100644 --- a/lazy-supplements/src/error.rs +++ b/lazy-supplements/src/error.rs @@ -18,6 +18,10 @@ pub enum Error { Multiaddr(#[from] libp2p::multiaddr::Error), #[error("Noise error: {0}")] Noise(#[from] libp2p::noise::Error), + #[error("Readline error: {0}")] + Readline(#[from] rustyline::error::ReadlineError), + #[error("Shell word split error: {0}")] + ShellWord(#[from] shell_words::ParseError), #[error("toml deserialization error: {0}")] TomlDe(#[from] toml::de::Error), #[error("toml serialization error: {0}")] diff --git a/lazy-supplements/src/main.rs b/lazy-supplements/src/main.rs index ced05a9..cdce186 100644 --- a/lazy-supplements/src/main.rs +++ b/lazy-supplements/src/main.rs @@ -1,5 +1,5 @@ use clap::{Parser, Subcommand}; -use lazy_supplements::{cli::{ConsoleArgs, InitArgs, NodeArgs, NodeCommand, ServerArgs}, *}; +use lazy_supplements::{cli::{ConsoleArgs, ConsoleCommands, InitArgs, NodeArgs, NodeCommand, ServerArgs}, *}; #[derive(Debug, Parser)] struct Cli { @@ -25,6 +25,6 @@ async fn main() { }, Command::Init(x) => x.init_config().await, Command::Server(x) => x.start_server().await.unwrap(), - Command::Console(x) => x.start_console().await.unwrap(), + Command::Console(x) => x.start_console(ConsoleCommands::default()).await.unwrap(), } } \ No newline at end of file