use std::{io, thread};
use std::sync::Arc;
use parking_lot::{RwLock, RwLockWriteGuard};
use tracing::{event, Level};
use crate::console::command::{Command, CommandError, CommandRegisterError, CommandRegistry, CommandTree};
use crate::console::log::ConsoleLog;
pub mod command;
#[cfg(feature = "gui")]
pub mod gui;
pub mod log;
pub struct Console {
commands: RwLock<CommandTree>,
log: Arc<ConsoleLog>,
}
impl Console {
#[inline]
pub fn builder() -> ConsoleBuilder {
ConsoleBuilder::default()
}
pub fn handle_command(&self, command: String) -> Result<(), CommandError> {
let parts = shlex::split(&command).unwrap();
self.commands.read().handle(parts.as_slice())
.map_err(|err| {
event!(Level::WARN, "Command failed: {err}");
err
})
}
pub fn registry(&self) -> ConsoleCommandRegistry {
ConsoleCommandRegistry {
inner: self.commands.write(),
}
}
#[inline]
pub fn log(&self) -> &Arc<ConsoleLog> { &self.log }
}
#[derive(Default)]
pub struct ConsoleBuilder {
log: Option<ConsoleLog>,
}
impl ConsoleBuilder {
pub fn log(mut self, log: ConsoleLog) -> Self {
self.log = Some(log);
self
}
pub fn build(self) -> Console {
Console {
commands: RwLock::new(CommandTree::default()),
log: Arc::new(self.log.unwrap_or_default()),
}
}
}
pub struct ConsoleCommandRegistry<'a> {
inner: RwLockWriteGuard<'a, CommandTree>,
}
impl CommandRegistry for ConsoleCommandRegistry<'_> {
fn register_command<S: Into<String>>(
&mut self,
key: S,
command: Box<dyn Command>,
) -> Result<&mut Self, CommandRegisterError> {
self.inner.register_command(key, command)?;
Ok(self)
}
fn register_alias<S: Into<String>, T: Into<String>>(
&mut self,
alias: S,
key: T,
) -> Result<&mut Self, CommandRegisterError> {
self.inner.register_alias(alias, key)?;
Ok(self)
}
}
pub struct ConsoleStdinReader {}
impl ConsoleStdinReader {
pub fn start(console: &Arc<Console>) {
let console = console.clone();
thread::spawn(move || {
for line in io::stdin().lines() {
let _ = console.handle_command(line.unwrap());
}
});
}
}