#![allow(clippy::borrowed_box)]
use crate::{
AnyOutput, ChainProcess, Dispatcher, Program, ProgramCollect, RenderResult,
error::{ChainProcessError, ProgramInternalExecuteError},
hint::{DispatcherNotFound, NoChainFound, ProgramEnd, RendererNotFound},
};
pub mod error;
pub async fn exec<C: ProgramCollect>(
program: Program<C>,
) -> Result<RenderResult, ProgramInternalExecuteError> {
let matched: (Box<dyn Dispatcher>, Vec<String>) = match match_user_input(&program) {
Ok(r) => (r.0.clone(), r.1),
Err(ProgramInternalExecuteError::DispatcherNotFound) => {
let disp: Box<dyn Dispatcher> = Box::new(DispatcherNotFound);
(disp, program.args)
}
Err(e) => return Err(e),
};
let (dispatcher, args) = matched;
let mut current = match handle_chain_process::<C>(dispatcher.begin(args)) {
Ok(Next::RenderResult(render_result)) => return Ok(render_result),
Ok(Next::AnyOutput(any)) => any,
Err(e) => return Err(e),
};
loop {
current = {
if C::has_chain(¤t) {
match handle_chain_process::<C>(C::do_chain(current).await) {
Ok(Next::RenderResult(render_result)) => return Ok(render_result),
Ok(Next::AnyOutput(any)) => any,
Err(e) => return Err(e),
}
}
else if C::has_renderer(¤t) {
let mut render_result = RenderResult::default();
C::render(current, &mut render_result);
return Ok(render_result);
}
else {
let disp: Box<dyn Dispatcher> = Box::new(RendererNotFound);
match handle_chain_process::<C>(disp.begin(vec![format!("{:?}", current.type_id)]))
{
Ok(Next::AnyOutput(any)) => any,
Ok(Next::RenderResult(result)) => return Ok(result),
Err(e) => return Err(e),
}
}
};
if current.is::<ProgramEnd>() || current.is::<NoChainFound>() {
break;
}
}
Ok(RenderResult::default())
}
fn match_user_input<C: ProgramCollect>(
program: &Program<C>,
) -> Result<(&Box<dyn Dispatcher>, Vec<String>), ProgramInternalExecuteError> {
let nodes = get_nodes(program);
let command = format!("{} ", program.args.join(" "));
let matching_nodes: Vec<&(String, &Box<dyn Dispatcher>)> = nodes
.iter()
.filter(|(node_str, _)| command.starts_with(&format!("{} ", node_str)))
.collect();
match matching_nodes.len() {
0 => {
Err(ProgramInternalExecuteError::DispatcherNotFound)
}
1 => {
let matched_prefix = matching_nodes[0];
let prefix_len = matched_prefix.0.split_whitespace().count();
let trimmed_args: Vec<String> = program.args.iter().skip(prefix_len).cloned().collect();
Ok((matched_prefix.1, trimmed_args))
}
_ => {
let matched_prefix = matching_nodes
.iter()
.max_by_key(|node| node.0.len())
.unwrap();
let prefix_len = matched_prefix.0.split_whitespace().count();
let trimmed_args: Vec<String> = program.args.iter().skip(prefix_len).cloned().collect();
Ok((matched_prefix.1, trimmed_args))
}
}
}
#[inline(always)]
fn render<C: ProgramCollect>(any: AnyOutput) -> RenderResult {
let mut render_result = RenderResult::default();
C::render(any, &mut render_result);
render_result
}
fn handle_chain_process<C: ProgramCollect>(
process: ChainProcess,
) -> Result<Next, ProgramInternalExecuteError> {
match process {
Ok(any) => Ok(Next::AnyOutput(any)),
Err(e) => match e {
ChainProcessError::Broken(any_output) => {
let render_result = render::<C>(any_output);
Ok(Next::RenderResult(render_result))
}
_ => Err(e.into()),
},
}
}
fn get_nodes<C: ProgramCollect>(program: &Program<C>) -> Vec<(String, &Box<dyn Dispatcher>)> {
program
.dispatcher
.iter()
.map(|disp| {
let node_str = disp
.node()
.to_string()
.split('.')
.collect::<Vec<_>>()
.join(" ");
(node_str, disp)
})
.collect()
}
enum Next {
RenderResult(RenderResult),
AnyOutput(AnyOutput),
}