// Doc Not Optimize #![allow(clippy::borrowed_box)] #![allow(clippy::too_many_lines)] use crate::{ AnyOutput, ChainProcess, NextProcess, Program, ProgramCollect, RenderResult, error::ProgramInternalExecuteError, hook::ProgramControls, }; #[doc(hidden)] pub mod error; #[might_be_async::func] pub fn exec(program: &'static Program) -> Result where C: ProgramCollect + Send + Sync, { let mut args = program.args.clone(); might_be_async::invoke!(exec_with_args(program, &mut args)) } #[might_be_async::func] pub fn exec_with_args( program: &'static Program, args: &mut Vec, ) -> Result where C: ProgramCollect + Send + Sync, { // Exit code let mut exit_code: i32 = 0; macro_rules! control { ($hook_call:expr, $current:expr) => { let __ccc = $hook_call; if let Some(r) = handle_program_control(program, __ccc, Some(&mut $current), &mut exit_code) { return Ok(r); } }; ($hook_call:expr) => { let __ccc = $hook_call; if let Some(r) = handle_program_control(program, __ccc, None, &mut exit_code) { return Ok(r); } }; } // Current let mut current = C::build_entry_fallback(vec![]); // Run hooks control!( program.run_hook_pre_dispatch(&mut crate::hook::HookPreDispatchInfo { arguments: &mut *args, }), current ); // Dispatch args let mut current = C::dispatch_args(args)?; // Run hook control!( program.run_hook_post_dispatch(&crate::hook::HookPostDispatchInfo { entry: ¤t.member_id, }), current ); let mut stop_next = false; // If the program has Help enabled, skip actual logic and jump to Help if program.user_context.help { let mut render_result = render_help::(program, current); // Run hook control!(program.run_hook_finish(&crate::hook::HookFinishInfo {})); render_result.exit_code = exit_code; return Ok(render_result); } loop { let final_exec = stop_next; current = { // If a chain exists, execute as a chain if C::has_chain(¤t) { // Run hook control!( program.run_hook_pre_chain(&crate::hook::HookPreChainInfo { input: ¤t.member_id, raw: current.inner.as_ref(), }), current ); match might_be_async::invoke!(C::do_chain(current)) { ChainProcess::Ok((any, NextProcess::Renderer)) => { { let mut render_result = render::(program, any); // Run hook control!(program.run_hook_finish(&crate::hook::HookFinishInfo {})); render_result.exit_code = exit_code; return Ok(render_result); }; } ChainProcess::Ok((mut any, NextProcess::Chain)) => { // Run hook control!( program.run_hook_post_chain(&crate::hook::HookPostChainInfo { output: &any }), any ); any } ChainProcess::Err(e) => { // Run hook control!( program.run_hook_finish(&crate::hook::HookFinishInfo {}), &mut C::build_empty_result() ); return Err(e.into()); } } } // If no chain exists, attempt to render else if C::has_renderer(¤t) { // Run hook control!( program.run_hook_pre_render(&crate::hook::HookPreRenderInfo { input: ¤t.member_id, raw: current.inner.as_ref(), }), current ); let mut render_result = render::(program, current); // Run hooks control!( program.run_hook_post_render(&crate::hook::HookPostRenderInfo { result: &render_result, }) ); control!(program.run_hook_finish(&crate::hook::HookFinishInfo {})); render_result.exit_code = exit_code; return Ok(render_result); } // No renderer exists else { stop_next = true; C::build_renderer_not_found(current.member_id) } }; if final_exec && stop_next { break; } } let mut render_result = RenderResult::default(); // Run hook control!( program.run_hook_finish(&crate::hook::HookFinishInfo {}), current ); render_result.exit_code = exit_code; Ok(render_result) } #[inline] pub(crate) fn handle_program_control>( program: &Program, controls: ProgramControls, mut current: Option<&mut AnyOutput>, exit_code: &mut i32, ) -> Option { for unit in controls { match unit { super::hook::ProgramControlUnit::OverrideExitCode(c) => *exit_code = c, super::hook::ProgramControlUnit::RouteToChain(any_output) => { if let Some(ref mut current) = current { **current = any_output; } } super::hook::ProgramControlUnit::RouteToRender(any_output) => { // Note: Hooks triggered by ProgramControl will not trigger ProgramControl again // Pre render let _ = program.run_hook_pre_render(&crate::hook::HookPreRenderInfo { input: &any_output.member_id, raw: any_output.inner.as_ref(), }); let mut r = render::(program, any_output); r.exit_code = *exit_code; // Post render program.run_hook_post_render(&crate::hook::HookPostRenderInfo { result: &r }); return Some(r); } super::hook::ProgramControlUnit::RouteToHelp(any_output) => { let mut r = render_help::(program, any_output); r.exit_code = *exit_code; return Some(r); } } } None } #[inline] #[allow(unused_variables)] fn render>(program: &Program, any: AnyOutput) -> RenderResult { #[cfg(not(feature = "structural_renderer"))] { C::render(any) } #[cfg(feature = "structural_renderer")] { #[allow(unreachable_patterns)] match program.structural_renderer_name { super::StructuralRendererSetting::Disable => C::render(any), _ => C::structural_render(any, &program.structural_renderer_name).unwrap(), } } } #[inline] #[allow(unused_variables)] fn render_help>( program: &Program, entry: AnyOutput, ) -> RenderResult { #[cfg(not(feature = "structural_renderer"))] { C::render_help(entry) } #[cfg(feature = "structural_renderer")] { #[allow(unreachable_patterns)] match program.structural_renderer_name { super::StructuralRendererSetting::Disable => C::render_help(entry), _ => RenderResult::default(), } } }