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;
use crate::util::priority::HasStaticPriority;
use crate::worker::{WorkQueue, WorkTask, WorkerPool};
pub mod command;
#[cfg(feature = "gui")]
pub mod gui;
pub mod log;
pub struct Console {
commands: Arc<RwLock<CommandTree>>,
log: Arc<ConsoleLog>,
work_queue: Arc<WorkQueue>,
}
impl Console {
#[inline]
pub fn builder<'a>() -> ConsoleBuilder<'a> {
ConsoleBuilder::default()
}
pub fn handle_command(&self, command: String) -> WorkTask<Result<(), CommandError>> {
let commands = self.commands.clone();
self.work_queue.execute(move || {
let parts = shlex::split(&command).unwrap();
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<'a> {
log: Option<ConsoleLog>,
work_queue_builder: Option<Box<dyn (FnOnce() -> Arc<WorkQueue>) + 'a>>,
}
impl<'a> ConsoleBuilder<'a> {
pub fn log(mut self, log: ConsoleLog) -> Self {
self.log = Some(log);
self
}
pub fn worker_config<PP>(
mut self,
pool_priority: PP,
workers: &'a WorkerPool<PP>,
) -> Self
where
PP: HasStaticPriority + Send + Sync + 'static,
{
self.work_queue_builder = Some(Box::new(move || {
let work_queue = Arc::new(WorkQueue::new());
workers.insert_source(pool_priority, Arc::downgrade(&work_queue));
work_queue
}));
self
}
pub fn build(self) -> Console {
let work_queue_builder = self.work_queue_builder
.expect(".worker_config() is required");
let work_queue = work_queue_builder();
Console {
commands: Arc::new(RwLock::new(CommandTree::default())),
log: Arc::new(self.log.unwrap_or_default()),
work_queue,
}
}
}
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());
}
});
}
}