diff options
| -rw-r--r-- | examples/example-pack-err/page.toml | 2 | ||||
| -rw-r--r-- | mingling_core/src/any/group.rs | 87 | ||||
| -rw-r--r-- | mingling_core/src/comp/comp_ctx.rs | 65 | ||||
| -rw-r--r-- | mingling_core/src/program/collection.rs | 3 | ||||
| -rw-r--r-- | mingling_core/src/program/collection/mock.rs | 105 | ||||
| -rw-r--r-- | mingling_core/src/program/setup.rs | 63 | ||||
| -rw-r--r-- | mingling_core/tests/test-all/tests/integration.rs | 76 | ||||
| -rw-r--r-- | mingling_core/tests/test-comp/tests/integration.rs | 55 | ||||
| -rw-r--r-- | mingling_macros/src/dispatcher.rs | 1 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 7 | ||||
| -rw-r--r-- | mling/src/cli.rs | 2 |
11 files changed, 205 insertions, 261 deletions
diff --git a/examples/example-pack-err/page.toml b/examples/example-pack-err/page.toml index 5534236..255bbdd 100644 --- a/examples/example-pack-err/page.toml +++ b/examples/example-pack-err/page.toml @@ -1,6 +1,6 @@ [example] id = "example-pack-err" -name = "pack_err!" +name = "Pack an Error" icon = "🛑" category = "macros" desc = """ diff --git a/mingling_core/src/any/group.rs b/mingling_core/src/any/group.rs index c251286..e9fce5e 100644 --- a/mingling_core/src/any/group.rs +++ b/mingling_core/src/any/group.rs @@ -1,21 +1,74 @@ -use crate::{AnyOutput, ChainProcess}; - -/// Used to mark a type with a unique enum ID, assisting dynamic dispatch -pub trait Groupped<Group> { - /// Returns the specific enum value representing its ID within that enum - fn member_id() -> Group; - - /// Converts the grouped item into a `ChainProcess` directed to the chain route. - /// - /// This wraps the item into an `AnyOutput` and routes it to the chain processing pipeline. - fn to_chain(self) -> ChainProcess<Group> { - AnyOutput::new(self).route_chain() +#[cfg(feature = "general_renderer")] +pub use general_renderer_groupped::*; + +#[cfg(not(feature = "general_renderer"))] +pub use groupped::*; + +#[cfg(feature = "general_renderer")] +mod general_renderer_groupped { + use serde::Serialize; + + use crate::{AnyOutput, ChainProcess}; + /// Used to mark a type with a unique enum ID, assisting dynamic dispatch + pub trait Groupped<Group> + where + Self: Sized + Serialize + 'static, + { + /// Returns the specific enum value representing its ID within that enum + fn member_id() -> Group; + + /// Converts the grouped item into a `ChainProcess` directed to the chain route. + /// + /// This wraps the item into an `AnyOutput` and routes it to the chain processing pipeline. + fn to_chain(self) -> ChainProcess<Group> + where + Self: Send + Serialize, + { + AnyOutput::new(self).route_chain() + } + + /// Converts the grouped item into a `ChainProcess` directed to the render route. + /// + /// This wraps the item into an `AnyOutput` and routes it to the render processing pipeline. + fn to_render(self) -> ChainProcess<Group> + where + Self: Send + Serialize, + { + AnyOutput::new(self).route_renderer() + } } +} + +#[cfg(not(feature = "general_renderer"))] +mod groupped { + use crate::{AnyOutput, ChainProcess}; + + /// Used to mark a type with a unique enum ID, assisting dynamic dispatch + pub trait Groupped<Group> + where + Self: Sized + 'static, + { + /// Returns the specific enum value representing its ID within that enum + fn member_id() -> Group; + + /// Converts the grouped item into a `ChainProcess` directed to the chain route. + /// + /// This wraps the item into an `AnyOutput` and routes it to the chain processing pipeline. + fn to_chain(self) -> ChainProcess<Group> + where + Self: Send, + { + AnyOutput::new(self).route_chain() + } - /// Converts the grouped item into a `ChainProcess` directed to the render route. - /// - /// This wraps the item into an `AnyOutput` and routes it to the render processing pipeline. - fn to_render(self) -> ChainProcess<Group> { - AnyOutput::new(self).route_renderer() + /// Converts the grouped item into a `ChainProcess` directed to the render route. + /// + /// This wraps the item into an `AnyOutput` and routes it to the render processing pipeline. + fn to_render(self) -> ChainProcess<Group> + where + Self: Send, + { + AnyOutput::new(self).route_renderer() + } } } diff --git a/mingling_core/src/comp/comp_ctx.rs b/mingling_core/src/comp/comp_ctx.rs index b9f9020..8d7fa5c 100644 --- a/mingling_core/src/comp/comp_ctx.rs +++ b/mingling_core/src/comp/comp_ctx.rs @@ -20,80 +20,27 @@ where #[cfg(test)] mod tests { - use super::*; - use crate::{AnyOutput, ChainProcess, Groupped, RenderResult}; - - /// Minimal mock collector that satisfies `C: ProgramCollect<Enum = C>` - /// by setting `Enum = Self`. - #[derive(Debug, Clone, PartialEq)] - struct MockCollect; - - impl Groupped<MockCollect> for MockCollect { - fn member_id() -> MockCollect { - MockCollect - } - } + use crate::MockProgramCollect; - impl ProgramCollect for MockCollect { - type Enum = MockCollect; - type ErrorDispatcherNotFound = MockCollect; - type ErrorRendererNotFound = MockCollect; - type ResultEmpty = MockCollect; - - fn build_renderer_not_found(_member_id: MockCollect) -> AnyOutput<MockCollect> { - unimplemented!() - } - fn build_dispatcher_not_found(_args: Vec<String>) -> AnyOutput<MockCollect> { - unimplemented!() - } - fn build_empty_result() -> AnyOutput<MockCollect> { - unimplemented!() - } - fn render(_any: AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn render_help(_any: AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn do_chain(_any: AnyOutput<MockCollect>) -> ChainProcess<MockCollect> { - unimplemented!() - } - #[cfg(feature = "comp")] - fn do_comp(_any: &AnyOutput<MockCollect>, _ctx: &crate::ShellContext) -> crate::Suggest { - unimplemented!() - } - fn has_renderer(_any: &AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - fn has_chain(_any: &AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - - #[cfg(feature = "general_renderer")] - fn general_render( - _any: AnyOutput<MockCollect>, - _setting: &crate::GeneralRendererSetting, - ) -> Result<RenderResult, crate::error::GeneralRendererSerializeError> { - unimplemented!() - } - } + use super::*; #[test] fn test_is_completing_with_comp_subcommand() { - let program: Program<MockCollect> = + let program: Program<MockProgramCollect> = Program::new_with_args(["program", "__comp", "some", "args"]); assert!(program.is_completing()); } #[test] fn test_is_completing_with_normal_subcommand() { - let program: Program<MockCollect> = Program::new_with_args(["program", "normal", "cmd"]); + let program: Program<MockProgramCollect> = + Program::new_with_args(["program", "normal", "cmd"]); assert!(!program.is_completing()); } #[test] fn test_is_completing_with_no_args() { - let program: Program<MockCollect> = Program::new_with_args(["program"]); + let program: Program<MockProgramCollect> = Program::new_with_args(["program"]); assert!(!program.is_completing()); } } diff --git a/mingling_core/src/program/collection.rs b/mingling_core/src/program/collection.rs index d3d18d6..078f736 100644 --- a/mingling_core/src/program/collection.rs +++ b/mingling_core/src/program/collection.rs @@ -12,6 +12,9 @@ use crate::{GeneralRendererSetting, error::GeneralRendererSerializeError}; #[cfg(feature = "comp")] use crate::{ShellContext, Suggest}; +mod mock; +pub use mock::*; + /// Collected program context /// /// Note: It is recommended to use the `gen_program!()` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to automatically create this type diff --git a/mingling_core/src/program/collection/mock.rs b/mingling_core/src/program/collection/mock.rs new file mode 100644 index 0000000..e1b3aa4 --- /dev/null +++ b/mingling_core/src/program/collection/mock.rs @@ -0,0 +1,105 @@ +pub use mock::*; + +mod mock { + #[cfg(feature = "async")] + use std::pin::Pin; + + #[cfg(feature = "dispatch_tree")] + use crate::Dispatcher; + + use crate::{AnyOutput, ChainProcess, Groupped, ProgramCollect, RenderResult}; + + #[cfg(feature = "general_renderer")] + use crate::{GeneralRendererSetting, error::GeneralRendererSerializeError}; + + #[cfg(feature = "comp")] + use crate::{ShellContext, Suggest}; + + #[cfg(feature = "general_renderer")] + use serde::Serialize; + + #[cfg_attr(feature = "general_renderer", derive(Serialize))] + #[allow(unused)] + pub enum MockProgramCollect { + Foo, + Bar, + } + + impl Groupped<MockProgramCollect> for MockProgramCollect { + fn member_id() -> MockProgramCollect { + MockProgramCollect::Foo + } + } + + impl ProgramCollect for MockProgramCollect { + type Enum = MockProgramCollect; + type ErrorDispatcherNotFound = MockProgramCollect; + type ErrorRendererNotFound = MockProgramCollect; + type ResultEmpty = MockProgramCollect; + + #[cfg(feature = "dispatch_tree")] + fn dispatch_args_trie( + _raw: &[String], + ) -> Result<AnyOutput<Self::Enum>, crate::error::ProgramInternalExecuteError> { + unreachable!() + } + + #[cfg(feature = "dispatch_tree")] + fn get_nodes() -> Vec<(String, &'static (dyn Dispatcher<Self::Enum> + Send + Sync))> { + unreachable!() + } + + fn build_renderer_not_found(_member_id: Self::Enum) -> AnyOutput<Self::Enum> { + unreachable!() + } + + fn build_dispatcher_not_found(_args: Vec<String>) -> AnyOutput<Self::Enum> { + unreachable!() + } + + fn build_empty_result() -> AnyOutput<Self::Enum> { + unreachable!() + } + + fn render(_any: AnyOutput<Self::Enum>, _r: &mut RenderResult) { + unreachable!() + } + + fn render_help(_any: AnyOutput<Self::Enum>, _r: &mut RenderResult) { + unreachable!() + } + + #[cfg(feature = "async")] + fn do_chain( + _any: AnyOutput<Self::Enum>, + ) -> Pin<Box<dyn Future<Output = ChainProcess<Self::Enum>> + Send>> { + unreachable!() + } + + #[cfg(not(feature = "async"))] + fn do_chain(_any: AnyOutput<Self::Enum>) -> ChainProcess<Self::Enum> { + unreachable!() + } + + #[cfg(feature = "comp")] + fn do_comp(_any: &AnyOutput<Self::Enum>, _ctx: &ShellContext) -> Suggest { + unreachable!() + } + + fn has_renderer(_any: &AnyOutput<Self::Enum>) -> bool { + unreachable!() + } + + fn has_chain(_any: &AnyOutput<Self::Enum>) -> bool { + unreachable!() + } + + #[cfg(feature = "general_renderer")] + fn general_render( + _any: AnyOutput<Self::Enum>, + _setting: &GeneralRendererSetting, + ) -> Result<RenderResult, GeneralRendererSerializeError> { + unreachable!() + } + } +} diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs index 2bfced1..f248fb6 100644 --- a/mingling_core/src/program/setup.rs +++ b/mingling_core/src/program/setup.rs @@ -20,69 +20,14 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{AnyOutput, ChainProcess, Groupped, RenderResult}; - - /// Minimal mock collector that satisfies `C: ProgramCollect<Enum = C>` - /// by setting `Enum = Self`. - #[derive(Debug, Clone, PartialEq)] - struct MockCollect; - - impl Groupped<MockCollect> for MockCollect { - fn member_id() -> MockCollect { - MockCollect - } - } - - impl ProgramCollect for MockCollect { - type Enum = MockCollect; - type ErrorDispatcherNotFound = MockCollect; - type ErrorRendererNotFound = MockCollect; - type ResultEmpty = MockCollect; - - fn build_renderer_not_found(_member_id: MockCollect) -> AnyOutput<MockCollect> { - unimplemented!() - } - fn build_dispatcher_not_found(_args: Vec<String>) -> AnyOutput<MockCollect> { - unimplemented!() - } - fn build_empty_result() -> AnyOutput<MockCollect> { - unimplemented!() - } - fn render(_any: AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn render_help(_any: AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn do_chain(_any: AnyOutput<MockCollect>) -> ChainProcess<MockCollect> { - unimplemented!() - } - #[cfg(feature = "comp")] - fn do_comp(_any: &AnyOutput<MockCollect>, _ctx: &crate::ShellContext) -> crate::Suggest { - unimplemented!() - } - fn has_renderer(_any: &AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - fn has_chain(_any: &AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - - #[cfg(feature = "general_renderer")] - fn general_render( - _any: AnyOutput<MockCollect>, - _setting: &crate::GeneralRendererSetting, - ) -> Result<RenderResult, crate::error::GeneralRendererSerializeError> { - unimplemented!() - } - } + use crate::MockProgramCollect; struct TestSetup { called: std::rc::Rc<std::cell::Cell<bool>>, } - impl ProgramSetup<MockCollect> for TestSetup { - fn setup(self, _program: &mut Program<MockCollect>) { + impl ProgramSetup<MockProgramCollect> for TestSetup { + fn setup(self, _program: &mut Program<MockProgramCollect>) { self.called.set(true); } } @@ -93,7 +38,7 @@ mod tests { let setup = TestSetup { called: std::rc::Rc::clone(&called), }; - let mut program: Program<MockCollect> = Program::new_with_args(["test"]); + let mut program: Program<MockProgramCollect> = Program::new_with_args(["test"]); program.with_setup(setup); assert!(called.get()); } diff --git a/mingling_core/tests/test-all/tests/integration.rs b/mingling_core/tests/test-all/tests/integration.rs index f910729..c622835 100644 --- a/mingling_core/tests/test-all/tests/integration.rs +++ b/mingling_core/tests/test-all/tests/integration.rs @@ -1,11 +1,10 @@ use mingling::Flag; use mingling::GeneralRenderer; use mingling::GeneralRendererSetting; -use mingling::Groupped; +use mingling::MockProgramCollect; use mingling::NextProcess; use mingling::Node; use mingling::Program; -use mingling::ProgramCollect; use mingling::RenderResult; use mingling::StringVec; use mingling::comp::{ShellContext, ShellFlag, Suggest}; @@ -14,73 +13,6 @@ use mingling::hook::ProgramHook; use serde::Serialize; use std::sync::atomic::{AtomicBool, Ordering}; -// MockCollect for is_completing tests - -#[derive(Debug, Clone, PartialEq)] -struct MockCollect; - -impl Groupped<MockCollect> for MockCollect { - fn member_id() -> MockCollect { - MockCollect - } -} - -impl ProgramCollect for MockCollect { - type Enum = MockCollect; - type ErrorDispatcherNotFound = MockCollect; - type ErrorRendererNotFound = MockCollect; - type ResultEmpty = MockCollect; - - fn build_renderer_not_found(_member_id: MockCollect) -> mingling::AnyOutput<MockCollect> { - unimplemented!() - } - fn build_dispatcher_not_found(_args: Vec<String>) -> mingling::AnyOutput<MockCollect> { - unimplemented!() - } - fn build_empty_result() -> mingling::AnyOutput<MockCollect> { - unimplemented!() - } - fn render(_any: mingling::AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn render_help(_any: mingling::AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn do_chain(_any: mingling::AnyOutput<MockCollect>) -> mingling::ChainProcess<MockCollect> { - unimplemented!() - } - fn do_comp(_any: &mingling::AnyOutput<MockCollect>, _ctx: &ShellContext) -> Suggest { - unimplemented!() - } - fn has_renderer(_any: &mingling::AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - fn has_chain(_any: &mingling::AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - - fn dispatch_args_trie( - _raw: &[String], - ) -> Result<mingling::AnyOutput<MockCollect>, mingling::error::ProgramInternalExecuteError> - { - unimplemented!() - } - - fn get_nodes() -> Vec<( - String, - &'static (dyn mingling::Dispatcher<MockCollect> + Send + Sync), - )> { - unimplemented!() - } - - fn general_render( - _any: mingling::AnyOutput<MockCollect>, - _setting: &GeneralRendererSetting, - ) -> Result<RenderResult, mingling::error::GeneralRendererSerializeError> { - unimplemented!() - } -} - // ShellContext #[test] @@ -192,13 +124,13 @@ fn test_general_renderer_json() { #[test] fn test_is_completing() { - let program: Program<MockCollect> = Program::new_with_args(["app", "__comp"]); + let program: Program<MockProgramCollect> = Program::new_with_args(["app", "__comp"]); assert!(program.is_completing()); } #[test] fn test_is_not_completing() { - let program: Program<MockCollect> = Program::new_with_args(["app", "greet"]); + let program: Program<MockProgramCollect> = Program::new_with_args(["app", "greet"]); assert!(!program.is_completing()); } @@ -208,7 +140,7 @@ fn test_is_not_completing() { fn test_hook_setup() { static CALLED: AtomicBool = AtomicBool::new(false); - let hook = ProgramHook::<MockCollect>::empty().on_begin(|| { + let hook = ProgramHook::<MockProgramCollect>::empty().on_begin(|| { CALLED.store(true, Ordering::SeqCst); }); diff --git a/mingling_core/tests/test-comp/tests/integration.rs b/mingling_core/tests/test-comp/tests/integration.rs index 4e6455e..37aa716 100644 --- a/mingling_core/tests/test-comp/tests/integration.rs +++ b/mingling_core/tests/test-comp/tests/integration.rs @@ -1,55 +1,7 @@ -use mingling::Groupped; +use mingling::MockProgramCollect; use mingling::Program; -use mingling::ProgramCollect; -use mingling::RenderResult; use mingling::comp::{ShellContext, ShellFlag, Suggest, SuggestItem}; -/// Minimal mock collector that satisfies `C: ProgramCollect<Enum = C>` -/// by setting `Enum = Self`. -#[derive(Debug, Clone, PartialEq)] -struct MockCollect; - -impl Groupped<MockCollect> for MockCollect { - fn member_id() -> MockCollect { - MockCollect - } -} - -impl ProgramCollect for MockCollect { - type Enum = MockCollect; - type ErrorDispatcherNotFound = MockCollect; - type ErrorRendererNotFound = MockCollect; - type ResultEmpty = MockCollect; - - fn build_renderer_not_found(_member_id: MockCollect) -> mingling::AnyOutput<MockCollect> { - unimplemented!() - } - fn build_dispatcher_not_found(_args: Vec<String>) -> mingling::AnyOutput<MockCollect> { - unimplemented!() - } - fn build_empty_result() -> mingling::AnyOutput<MockCollect> { - unimplemented!() - } - fn render(_any: mingling::AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn render_help(_any: mingling::AnyOutput<MockCollect>, _r: &mut RenderResult) { - unimplemented!() - } - fn do_chain(_any: mingling::AnyOutput<MockCollect>) -> mingling::ChainProcess<MockCollect> { - unimplemented!() - } - fn do_comp(_any: &mingling::AnyOutput<MockCollect>, _ctx: &ShellContext) -> Suggest { - unimplemented!() - } - fn has_renderer(_any: &mingling::AnyOutput<MockCollect>) -> bool { - unimplemented!() - } - fn has_chain(_any: &mingling::AnyOutput<MockCollect>) -> bool { - unimplemented!() - } -} - #[test] fn test_shell_context_parsing_full() { let args = vec![ @@ -112,12 +64,13 @@ fn test_suggest_item_with_description() { #[test] fn test_program_is_completing() { - let program: Program<MockCollect> = Program::new_with_args(["myapp", "__comp", "hello", ""]); + let program: Program<MockProgramCollect> = + Program::new_with_args(["myapp", "__comp", "hello", ""]); assert!(program.is_completing()); } #[test] fn test_program_is_not_completing() { - let program: Program<MockCollect> = Program::new_with_args(["myapp", "hello"]); + let program: Program<MockProgramCollect> = Program::new_with_args(["myapp", "hello"]); assert!(!program.is_completing()); } diff --git a/mingling_macros/src/dispatcher.rs b/mingling_macros/src/dispatcher.rs index 7e973eb..6bf10f1 100644 --- a/mingling_macros/src/dispatcher.rs +++ b/mingling_macros/src/dispatcher.rs @@ -223,6 +223,7 @@ pub fn dispatcher(input: TokenStream) -> TokenStream { ::mingling::macros::node!(#command_name_str) } fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<#program_path> { + use ::mingling::Groupped; #pack::new(args).to_chain() } fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<#program_path>> { diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 408450a..9880cd6 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -480,7 +480,7 @@ pub fn route(input: TokenStream) -> TokenStream { #[proc_macro] pub fn empty_result(_input: TokenStream) -> TokenStream { let expanded = quote! { - crate::ResultEmpty::new(()).to_chain() + <crate::ResultEmpty as ::mingling::Groupped::<crate::ThisProgram>>::to_chain(crate::ResultEmpty::new(())) }; TokenStream::from(expanded) } @@ -1413,6 +1413,8 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { #[doc(hidden)] #[::mingling::macros::chain(#name)] pub async fn __exec_completion(prev: CompletionContext) -> Next { + use ::mingling::Groupped; + let read_ctx = ::mingling::ShellContext::try_from(prev.inner); match read_ctx { Ok(ctx) => { @@ -1429,6 +1431,8 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { #[doc(hidden)] #[::mingling::macros::chain(#name)] pub fn __exec_completion(prev: CompletionContext) -> Next { + use ::mingling::Groupped; + let read_ctx = ::mingling::ShellContext::try_from(prev.inner); match read_ctx { Ok(ctx) => { @@ -1452,6 +1456,7 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { #[doc(hidden)] mod __internal_completion_mod { use super::#name; + use ::mingling::Groupped; ::mingling::macros::dispatcher!(#name, "__comp", CMDCompletion => CompletionContext); ::mingling::macros::pack!( #name, diff --git a/mling/src/cli.rs b/mling/src/cli.rs index 6dc7b9e..123cca3 100644 --- a/mling/src/cli.rs +++ b/mling/src/cli.rs @@ -8,7 +8,7 @@ use crate::{ }; use colored::Colorize; use mingling::{ - Program, + Groupped, Program, hook::ProgramHook, macros::{chain, help, pack, program_setup, r_println, renderer}, res::ResExitCode, |
