aboutsummaryrefslogtreecommitdiff
path: root/mingling_core
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-05-09 16:42:50 +0800
committer魏曹先生 <1992414357@qq.com>2026-05-09 17:13:35 +0800
commit10bc4ca7a4b3f14cfb57bf72a6da8aaa1490acf3 (patch)
tree8b985dad38cfcd4c88af962b3e163131b258a381 /mingling_core
parent89b5132b8c17b6eba45b1bd49aa18b0c02016387 (diff)
Add lifecycle hooks to program execution
Diffstat (limited to 'mingling_core')
-rw-r--r--mingling_core/src/asset/dispatcher.rs7
-rw-r--r--mingling_core/src/program.rs42
-rw-r--r--mingling_core/src/program/config.rs15
-rw-r--r--mingling_core/src/program/exec.rs114
-rw-r--r--mingling_core/src/program/flag.rs2
-rw-r--r--mingling_core/src/program/hook.rs349
-rw-r--r--mingling_core/src/program/setup.rs5
-rw-r--r--mingling_core/src/program/setup/general_renderer.rs2
-rw-r--r--mingling_core/src/renderer/render_result.rs1
9 files changed, 507 insertions, 30 deletions
diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs
index cf8fcca..95b3305 100644
--- a/mingling_core/src/asset/dispatcher.rs
+++ b/mingling_core/src/asset/dispatcher.rs
@@ -1,6 +1,6 @@
use std::fmt::Display;
-use crate::{ChainProcess, Program, asset::node::Node};
+use crate::{ChainProcess, Program, ProgramCollect, asset::node::Node};
/// Dispatches user input commands to specific [ChainProcess](./enum.ChainProcess.html)
///
@@ -27,7 +27,10 @@ where
}
}
-impl<C: crate::program::ProgramCollect> Program<C> {
+impl<C> Program<C>
+where
+ C: ProgramCollect<Enum = C>,
+{
/// Adds a dispatcher to the program.
#[cfg(not(feature = "dispatch_tree"))]
pub fn with_dispatcher<Disp>(&mut self, dispatcher: Disp)
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index 4d943cf..0d23770 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -11,6 +11,7 @@ use crate::{
AnyOutput, ChainProcess, GlobalResources, Groupped, RenderResult,
asset::dispatcher::Dispatcher,
error::{ChainProcessError, ProgramExecuteError},
+ hook::{ProgramAnonymousHook, ProgramHook},
};
use std::{
collections::HashMap,
@@ -24,6 +25,8 @@ use std::pin::Pin;
#[doc(hidden)]
pub mod exec;
#[doc(hidden)]
+pub mod hook;
+#[doc(hidden)]
pub mod setup;
mod config;
@@ -41,7 +44,7 @@ static THIS_PROGRAM: OnceLock<Option<Box<dyn std::any::Any + Send + Sync>>> = On
/// Returns a reference to the current program instance, panics if not set.
pub fn this<C>() -> &'static Program<C>
where
- C: ProgramCollect + 'static,
+ C: ProgramCollect<Enum = C> + 'static,
{
try_get_this_program().expect("Program not initialized")
}
@@ -49,7 +52,7 @@ where
/// Returns a reference to the current program instance, if set.
fn try_get_this_program<C>() -> Option<&'static Program<C>>
where
- C: ProgramCollect + 'static,
+ C: ProgramCollect<Enum = C> + 'static,
{
THIS_PROGRAM.get()?.as_ref()?.downcast_ref::<Program<C>>()
}
@@ -58,7 +61,7 @@ where
#[derive(Default)]
pub struct Program<C>
where
- C: ProgramCollect,
+ C: ProgramCollect<Enum = C>,
{
pub(crate) collect: std::marker::PhantomData<C>,
@@ -73,6 +76,9 @@ where
#[cfg(feature = "general_renderer")]
pub general_renderer_name: GeneralRendererSetting,
+ pub(crate) hooks: Vec<ProgramHook<C>>,
+ pub(crate) anonymous_hooks: Vec<ProgramAnonymousHook>,
+
pub(crate) resources: GlobalResources,
}
@@ -113,6 +119,9 @@ where
#[cfg(feature = "general_renderer")]
general_renderer_name: GeneralRendererSetting::Disable,
+ hooks: Vec::new(),
+ anonymous_hooks: Vec::new(),
+
resources: Arc::new(Mutex::new(HashMap::new())),
}
}
@@ -167,7 +176,7 @@ where
#[cfg(feature = "async")]
impl<C> Program<C>
where
- C: ProgramCollect<Enum = C>,
+ C: ProgramCollect<Enum = C> + std::fmt::Display,
{
/// Sets the current program instance and runs the provided async function.
async fn set_instance_and_run<F, Fut>(self, f: F) -> Fut::Output
@@ -237,7 +246,7 @@ where
#[cfg(not(feature = "async"))]
impl<C> Program<C>
where
- C: ProgramCollect<Enum = C>,
+ C: ProgramCollect<Enum = C> + Display,
{
/// Sets the current program instance and runs the provided function.
fn set_instance_and_run<F, R>(self, f: F) -> R
@@ -266,7 +275,7 @@ where
}
/// Run the command line program
- pub fn exec(self)
+ pub fn exec(self) -> i32
where
C: 'static + Send + Sync,
{
@@ -276,29 +285,44 @@ where
Err(e) => match e {
ProgramExecuteError::DispatcherNotFound => {
eprintln!("Dispatcher not found");
- return;
+ return 1;
}
ProgramExecuteError::RendererNotFound(renderer_name) => {
eprintln!("Renderer `{}` not found", renderer_name);
- return;
+ return 1;
}
ProgramExecuteError::Other(e) => {
eprintln!("{}", e);
- return;
+ return 1;
}
},
};
// Render result
if stdout_setting.render_output && !result.is_empty() {
+ let exit_code = result.exit_code;
print!("{}", result);
+
if let Err(e) = std::io::Write::flush(&mut std::io::stdout())
&& stdout_setting.error_output
{
eprintln!("{}", e);
+ 1
+ } else {
+ exit_code
}
+ } else {
+ 0
}
}
+
+ /// Run the command line program, then exit
+ pub fn exec_and_exit(self)
+ where
+ C: 'static + Send + Sync,
+ {
+ std::process::exit(self.exec())
+ }
}
/// Collected program context
diff --git a/mingling_core/src/program/config.rs b/mingling_core/src/program/config.rs
index ac541fd..35b9392 100644
--- a/mingling_core/src/program/config.rs
+++ b/mingling_core/src/program/config.rs
@@ -35,13 +35,26 @@ impl Default for ProgramStdoutSetting {
}
/// Program user context
-#[derive(Debug, Clone, Default)]
+#[derive(Debug, Clone)]
pub struct ProgramUserContext {
/// View help information instead of running the command
pub help: bool,
/// Skip user confirmation step
pub confirm: bool,
+
+ /// Execute hooks during the program lifecycle
+ pub run_hook: bool,
+}
+
+impl Default for ProgramUserContext {
+ fn default() -> Self {
+ Self {
+ help: false,
+ confirm: false,
+ run_hook: true,
+ }
+ }
}
#[cfg(feature = "general_renderer")]
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index 001b250..acd1bd6 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -13,19 +13,31 @@ pub async fn exec<C>(
program: &'static Program<C>,
) -> Result<RenderResult, ProgramInternalExecuteError>
where
- C: ProgramCollect<Enum = C>,
+ C: ProgramCollect<Enum = C> + std::fmt::Display,
{
+ // Run hooks
+ program.run_hook_on_begin();
+ program.run_hook_pre_dispatch(&program.args);
+
#[cfg(not(feature = "dispatch_tree"))]
let mut current = dispatch_args_dynamic(program, &program.args)?;
#[cfg(feature = "dispatch_tree")]
let mut current = C::dispatch_args_trie(&program.args)?;
+ // Run hook
+ program.run_hook_post_dispatch(&current.member_id);
+
let mut stop_next = false;
// If the program has Help enabled, skip actual logic and jump to Help
if program.user_context.help {
- return Ok(render_help::<C>(program, current));
+ let mut render_result = render_help::<C>(program, current);
+
+ // Run hook
+ render_result.exit_code = program.run_hook_finish();
+
+ return Ok(render_result);
}
loop {
@@ -34,17 +46,42 @@ where
current = {
// If a chain exists, execute as a chain
if C::has_chain(&current) {
+ // Run hook
+ program.run_hook_pre_chain(&current.member_id, current.inner.as_ref());
+
match C::do_chain(current).await {
ChainProcess::Ok((any, Next::Renderer)) => {
- return Ok(render::<C>(program, any));
+ let mut render_result = render::<C>(program, any);
+
+ // Run hook
+ render_result.exit_code = program.run_hook_finish();
+
+ return Ok(render_result);
+ }
+ ChainProcess::Ok((any, Next::Chain)) => {
+ // Run hook
+ program.run_hook_post_chain(&any);
+ any
+ }
+ ChainProcess::Err(e) => {
+ // Run hook
+ program.run_hook_finish();
+ return Err(e.into());
}
- ChainProcess::Ok((any, Next::Chain)) => any,
- ChainProcess::Err(e) => return Err(e.into()),
}
}
// If no chain exists, attempt to render
else if C::has_renderer(&current) {
- return Ok(render::<C>(program, current));
+ // Run hook
+ program.run_hook_pre_render(&current.member_id, current.inner.as_ref());
+
+ let mut render_result = render::<C>(program, current);
+
+ // Run hooks
+ program.run_hook_post_render(&render_result);
+ render_result.exit_code = program.run_hook_finish();
+
+ return Ok(render_result);
}
// No renderer exists
else {
@@ -57,25 +94,42 @@ where
break;
}
}
- Ok(RenderResult::default())
+ let mut render_result = RenderResult::default();
+
+ // Run hook
+ render_result.exit_code = program.run_hook_finish();
+
+ Ok(render_result)
}
#[cfg(not(feature = "async"))]
pub fn exec<C>(program: &'static Program<C>) -> Result<RenderResult, ProgramInternalExecuteError>
where
- C: ProgramCollect<Enum = C>,
+ C: ProgramCollect<Enum = C> + std::fmt::Display,
{
+ // Run hooks
+ program.run_hook_on_begin();
+ program.run_hook_pre_dispatch(&program.args);
+
#[cfg(not(feature = "dispatch_tree"))]
let mut current = dispatch_args_dynamic(program, &program.args)?;
#[cfg(feature = "dispatch_tree")]
let mut current = C::dispatch_args_trie(&program.args)?;
+ // Run hook
+ program.run_hook_post_dispatch(&current.member_id);
+
let mut stop_next = false;
// If the program has Help enabled, skip actual logic and jump to Help
if program.user_context.help {
- return Ok(render_help::<C>(program, current));
+ let mut render_result = render_help::<C>(program, current);
+
+ // Run hook
+ render_result.exit_code = program.run_hook_finish();
+
+ return Ok(render_result);
}
loop {
@@ -84,17 +138,44 @@ where
current = {
// If a chain exists, execute as a chain
if C::has_chain(&current) {
+ // Run hook
+ program.run_hook_pre_chain(&current.member_id, current.inner.as_ref());
+
match C::do_chain(current) {
ChainProcess::Ok((any, Next::Renderer)) => {
- return Ok(render::<C>(program, any));
+ {
+ let mut render_result = render::<C>(program, any);
+
+ // Run hook
+ render_result.exit_code = program.run_hook_finish();
+
+ return Ok(render_result);
+ };
+ }
+ ChainProcess::Ok((any, Next::Chain)) => {
+ // Run hook
+ program.run_hook_post_chain(&any);
+ any
+ }
+ ChainProcess::Err(e) => {
+ // Run hook
+ program.run_hook_finish();
+ return Err(e.into());
}
- ChainProcess::Ok((any, Next::Chain)) => any,
- ChainProcess::Err(e) => return Err(e.into()),
}
}
// If no chain exists, attempt to render
else if C::has_renderer(&current) {
- return Ok(render::<C>(program, current));
+ // Run hook
+ program.run_hook_pre_render(&current.member_id, current.inner.as_ref());
+
+ let mut render_result = render::<C>(program, current);
+
+ // Run hooks
+ program.run_hook_post_render(&render_result);
+ render_result.exit_code = program.run_hook_finish();
+
+ return Ok(render_result);
}
// No renderer exists
else {
@@ -107,7 +188,12 @@ where
break;
}
}
- Ok(RenderResult::default())
+ let mut render_result = RenderResult::default();
+
+ // Run hook
+ render_result.exit_code = program.run_hook_finish();
+
+ Ok(render_result)
}
/// Dynamically dispatch input arguments to registered entry types
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 0ab8d1c..210f2d6 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -474,7 +474,7 @@ mod tests {
impl<C> Program<C>
where
- C: ProgramCollect,
+ C: ProgramCollect<Enum = C>,
{
/// Registers a global argument (with value) and its handler.
pub fn global_argument<F, A>(&mut self, arguments: A, mut do_fn: F)
diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs
new file mode 100644
index 0000000..d422e12
--- /dev/null
+++ b/mingling_core/src/program/hook.rs
@@ -0,0 +1,349 @@
+use std::{any::Any, fmt::Display};
+
+use crate::{AnyOutput, Program, ProgramCollect, RenderResult};
+
+#[derive(Default)]
+pub struct ProgramHook<C>
+where
+ C: ProgramCollect<Enum = C>,
+{
+ /// Executes when the program starts running
+ pub begin: Option<fn()>,
+
+ /// Executes before the program dispatches
+ pub pre_dispatch: Option<fn(args: &Vec<String>)>,
+
+ /// Executes after the program dispatches
+ pub post_dispatch: Option<fn(entry: &C)>,
+
+ /// Executes before the type enters the chain
+ pub pre_chain: Option<fn(input: &C, raw: &dyn Any)>,
+
+ /// Executes after the chain processing for the type ends
+ pub post_chain: Option<fn(output: &AnyOutput<C>)>,
+
+ /// Executes before the type enters the renderer
+ pub pre_render: Option<fn(input: &C, raw: &dyn Any)>,
+
+ /// Executes after the type enters the renderer
+ pub post_render: Option<fn(result: &RenderResult)>,
+
+ /// Executes before the program ends
+ pub finish: Option<fn() -> i32>,
+}
+
+#[derive(Default)]
+pub struct ProgramAnonymousHook {
+ /// Executes when the program starts running
+ pub begin: Option<fn()>,
+
+ /// Executes before the program dispatches
+ pub pre_dispatch: Option<fn(args: &Vec<String>)>,
+
+ /// Executes after the program dispatches
+ pub post_dispatch: Option<fn(entry_name: &str)>,
+
+ /// Executes before the type enters the chain
+ pub pre_chain: Option<fn(input_name: &str, raw: &dyn Any)>,
+
+ /// Executes after the chain processing for the type ends
+ pub post_chain: Option<fn(output_name: &str)>,
+
+ /// Executes before the type enters the renderer
+ pub pre_render: Option<fn(input_name: &str, raw: &dyn Any)>,
+
+ /// Executes after the type enters the renderer
+ pub post_render: Option<fn(result: &RenderResult)>,
+
+ /// Executes before the program ends
+ pub finish: Option<fn()>,
+}
+
+impl<C> Program<C>
+where
+ C: ProgramCollect<Enum = C> + Display,
+{
+ /// Adds a typed hook to the program. The hook will be called at the appropriate
+ /// lifecycle events.
+ pub fn with_hook(&mut self, hook: ProgramHook<C>) {
+ self.hooks.push(hook);
+ }
+
+ /// Adds an anonymous hook to the program. The hook will be called at the appropriate
+ /// lifecycle events, but receives string representations instead of typed references.
+ pub fn with_hook_anonymous(&mut self, hook: ProgramAnonymousHook) {
+ self.anonymous_hooks.push(hook);
+ }
+
+ pub(crate) fn run_hook_on_begin(&self) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(begin) = hook.begin {
+ begin()
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(begin) = anonymous_hook.begin {
+ begin()
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_pre_dispatch(&self, args: &Vec<String>) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(pre_dispatch) = hook.pre_dispatch {
+ pre_dispatch(args)
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(pre_dispatch) = anonymous_hook.pre_dispatch {
+ pre_dispatch(args)
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_post_dispatch(&self, entry: &C) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(post_dispatch) = hook.post_dispatch {
+ post_dispatch(entry)
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(post_dispatch) = anonymous_hook.post_dispatch {
+ post_dispatch(entry.to_string().as_str())
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_pre_chain(&self, input: &C, raw: &dyn Any) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(pre_chain) = hook.pre_chain {
+ pre_chain(input, raw)
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(pre_chain) = anonymous_hook.pre_chain {
+ pre_chain(input.to_string().as_str(), raw)
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_post_chain(&self, output: &AnyOutput<C>) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(post_chain) = hook.post_chain {
+ post_chain(output)
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(post_chain) = anonymous_hook.post_chain {
+ post_chain(output.member_id.to_string().as_str())
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_pre_render(&self, input: &C, raw: &dyn Any) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(pre_render) = hook.pre_render {
+ pre_render(input, raw)
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(pre_render) = anonymous_hook.pre_render {
+ pre_render(input.to_string().as_str(), raw)
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_post_render(&self, result: &RenderResult) {
+ if !self.user_context.run_hook {
+ return;
+ }
+
+ for hook in &self.hooks {
+ if let Some(post_render) = hook.post_render {
+ post_render(result)
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(post_render) = anonymous_hook.post_render {
+ post_render(result)
+ }
+ }
+ }
+
+ pub(crate) fn run_hook_finish(&self) -> i32 {
+ if !self.user_context.run_hook {
+ return 0;
+ }
+
+ let mut exit_code = 0;
+ for hook in &self.hooks {
+ if let Some(finish) = hook.finish {
+ exit_code = finish();
+ if exit_code != 0 {
+ return exit_code;
+ }
+ }
+ }
+ for anonymous_hook in &self.anonymous_hooks {
+ if let Some(finish) = anonymous_hook.finish {
+ finish();
+ }
+ }
+ exit_code
+ }
+}
+
+impl<C> ProgramHook<C>
+where
+ C: ProgramCollect<Enum = C>,
+{
+ /// Creates a new empty hook set with no handlers.
+ pub fn empty() -> Self {
+ Self {
+ begin: None,
+ pre_dispatch: None,
+ post_dispatch: None,
+ pre_chain: None,
+ post_chain: None,
+ pre_render: None,
+ post_render: None,
+ finish: None,
+ }
+ }
+
+ /// Sets the handler for the `begin` event.
+ pub fn on_begin(mut self, handler: fn()) -> Self {
+ let _ = self.begin.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `pre_dispatch` event.
+ pub fn on_pre_dispatch(mut self, handler: fn(args: &Vec<String>)) -> Self {
+ let _ = self.pre_dispatch.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `post_dispatch` event.
+ pub fn on_post_dispatch(mut self, handler: fn(entry: &C)) -> Self {
+ let _ = self.post_dispatch.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `pre_chain` event.
+ pub fn on_pre_chain(mut self, handler: fn(input: &C, raw: &dyn Any)) -> Self {
+ let _ = self.pre_chain.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `post_chain` event.
+ pub fn on_post_chain(mut self, handler: fn(output: &AnyOutput<C>)) -> Self {
+ let _ = self.post_chain.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `pre_render` event.
+ pub fn on_pre_render(mut self, handler: fn(input: &C, raw: &dyn Any)) -> Self {
+ let _ = self.pre_render.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `post_render` event.
+ pub fn on_post_render(mut self, handler: fn(result: &RenderResult)) -> Self {
+ let _ = self.post_render.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `finish` event.
+ pub fn on_finish(mut self, handler: fn() -> i32) -> Self {
+ let _ = self.finish.insert(handler);
+ self
+ }
+}
+
+impl ProgramAnonymousHook {
+ /// Creates a new empty hook set with no handlers.
+ pub fn empty() -> Self {
+ Self {
+ begin: None,
+ pre_dispatch: None,
+ post_dispatch: None,
+ pre_chain: None,
+ post_chain: None,
+ pre_render: None,
+ post_render: None,
+ finish: None,
+ }
+ }
+
+ /// Sets the handler for the `begin` event.
+ pub fn on_begin(mut self, handler: fn()) -> Self {
+ let _ = self.begin.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `pre_dispatch` event.
+ pub fn on_pre_dispatch(mut self, handler: fn(args: &Vec<String>)) -> Self {
+ let _ = self.pre_dispatch.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `post_dispatch` event.
+ pub fn on_post_dispatch(mut self, handler: fn(entry_name: &str)) -> Self {
+ let _ = self.post_dispatch.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `pre_chain` event.
+ pub fn on_pre_chain(mut self, handler: fn(input_name: &str, raw: &dyn Any)) -> Self {
+ let _ = self.pre_chain.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `post_chain` event.
+ pub fn on_post_chain(mut self, handler: fn(output_name: &str)) -> Self {
+ let _ = self.post_chain.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `pre_render` event.
+ pub fn on_pre_render(mut self, handler: fn(input_name: &str, raw: &dyn Any)) -> Self {
+ let _ = self.pre_render.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `post_render` event.
+ pub fn on_post_render(mut self, handler: fn(result: &RenderResult)) -> Self {
+ let _ = self.post_render.insert(handler);
+ self
+ }
+
+ /// Sets the handler for the `finish` event.
+ pub fn on_finish(mut self, handler: fn()) -> Self {
+ let _ = self.finish.insert(handler);
+ self
+ }
+}
diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs
index 86228b9..28aa49b 100644
--- a/mingling_core/src/program/setup.rs
+++ b/mingling_core/src/program/setup.rs
@@ -5,19 +5,20 @@ pub use basic::*;
#[cfg(feature = "general_renderer")]
mod general_renderer;
+
#[cfg(feature = "general_renderer")]
pub use general_renderer::*;
pub trait ProgramSetup<C>
where
- C: ProgramCollect,
+ C: ProgramCollect<Enum = C>,
{
fn setup(&mut self, program: &mut Program<C>);
}
impl<C> Program<C>
where
- C: ProgramCollect,
+ C: ProgramCollect<Enum = C>,
{
/// Load and execute init logic
pub fn with_setup<S: ProgramSetup<C> + 'static>(&mut self, mut setup: S) -> S {
diff --git a/mingling_core/src/program/setup/general_renderer.rs b/mingling_core/src/program/setup/general_renderer.rs
index 299aae3..d2666da 100644
--- a/mingling_core/src/program/setup/general_renderer.rs
+++ b/mingling_core/src/program/setup/general_renderer.rs
@@ -32,7 +32,7 @@ pub struct GeneralRendererSetup;
impl<C> ProgramSetup<C> for GeneralRendererSetup
where
- C: ProgramCollect,
+ C: ProgramCollect<Enum = C>,
{
fn setup(&mut self, program: &mut Program<C>) {
program.global_flag("--json", |p| {
diff --git a/mingling_core/src/renderer/render_result.rs b/mingling_core/src/renderer/render_result.rs
index 3bde1ab..2bf159a 100644
--- a/mingling_core/src/renderer/render_result.rs
+++ b/mingling_core/src/renderer/render_result.rs
@@ -8,6 +8,7 @@ use std::{
#[derive(Default, Debug, PartialEq)]
pub struct RenderResult {
render_text: String,
+ pub exit_code: i32,
}
impl Write for RenderResult {