diff options
Diffstat (limited to 'mingling')
| -rw-r--r-- | mingling/Cargo.toml | 2 | ||||
| -rw-r--r-- | mingling/src/constants.rs | 8 | ||||
| -rw-r--r-- | mingling/src/constants/exit_codes.rs | 14 | ||||
| -rw-r--r-- | mingling/src/constants/picker.rs (renamed from mingling/src/setups/picker/consts.rs) | 11 | ||||
| -rw-r--r-- | mingling/src/lib.rs | 11 | ||||
| -rw-r--r-- | mingling/src/picker.rs | 13 | ||||
| -rw-r--r-- | mingling/src/picker/entry_picker.rs | 126 | ||||
| -rw-r--r-- | mingling/src/picker/global.rs | 35 | ||||
| -rw-r--r-- | mingling/src/setups/picker.rs | 3 | ||||
| -rw-r--r-- | mingling/src/setups/picker/basic.rs | 39 | ||||
| -rw-r--r-- | mingling/src/setups/picker/structural_renderer.rs | 93 |
11 files changed, 260 insertions, 95 deletions
diff --git a/mingling/Cargo.toml b/mingling/Cargo.toml index adf900f..6945b67 100644 --- a/mingling/Cargo.toml +++ b/mingling/Cargo.toml @@ -50,7 +50,7 @@ dispatch_tree = ["mingling_core/dispatch_tree", "mingling_macros/dispatch_tree"] repl = ["mingling_core/repl", "mingling_macros/repl"] comp = ["mingling_core/comp", "mingling_macros/comp"] parser = ["dep:size"] -picker = ["dep:arg-picker", "arg-picker/mingling_support"] +picker = ["dep:arg-picker"] pathf = ["mingling_core/pathf", "mingling_macros/pathf"] structural_renderer = [ diff --git a/mingling/src/constants.rs b/mingling/src/constants.rs new file mode 100644 index 0000000..65d02e8 --- /dev/null +++ b/mingling/src/constants.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "picker")] +mod picker; + +#[cfg(feature = "picker")] +pub use picker::*; + +mod exit_codes; +pub use exit_codes::*; diff --git a/mingling/src/constants/exit_codes.rs b/mingling/src/constants/exit_codes.rs new file mode 100644 index 0000000..2359f42 --- /dev/null +++ b/mingling/src/constants/exit_codes.rs @@ -0,0 +1,14 @@ +/// Exit code indicating successful command execution. +pub const EXIT_SUCCESS: i32 = 0; + +/// Exit code for general errors. +pub const EXIT_GENERAL_ERR: i32 = 1; + +/// Exit code for incorrect command usage (or invalid arguments). +pub const EXIT_USAGE_ERR: i32 = 2; + +/// Exit code indicating permission denied (or the command is not executable). +pub const EXIT_PERM_DENIED: i32 = 126; + +/// Exit code for command not found (or PATH error). +pub const EXIT_CMD_NOT_FOUND: i32 = 127; diff --git a/mingling/src/setups/picker/consts.rs b/mingling/src/constants/picker.rs index 5e93f3f..f2a448b 100644 --- a/mingling/src/setups/picker/consts.rs +++ b/mingling/src/constants/picker.rs @@ -45,6 +45,17 @@ pub const CONFIRM_FLAG: PickerArg<Flag> = PickerArg::<Flag> { internal_type: PhantomData, }; +/// Renderer flag: explicitly specify the Structural Renderer argument. +/// - `full`: `["renderer"]` +/// - `short`: (none) +#[cfg(feature = "structural_renderer")] +pub const RENDERER_ARG: PickerArg<String> = PickerArg::<String> { + full: &["renderer"], + short: None, + positional: false, + internal_type: PhantomData, +}; + /// JSON flag: enable JSON output format. /// - `full`: `["json"]` /// - `short`: (none) diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index 718d282..499390d 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -16,12 +16,13 @@ pub mod parser; /// `Mingling` argument parser (Picker2) #[cfg(feature = "picker")] -pub mod picker { - pub use arg_picker::*; +pub mod picker; - pub mod parselib { - pub use arg_picker::parselib::*; - } +mod constants; + +/// Constants used throughout the Mingling framework. +pub mod consts { + pub use crate::constants::*; } /// Re-export of all macros from `mingling_macros`. diff --git a/mingling/src/picker.rs b/mingling/src/picker.rs new file mode 100644 index 0000000..f370656 --- /dev/null +++ b/mingling/src/picker.rs @@ -0,0 +1,13 @@ +/// Provides the specific parsing logic for command-line arguments and common utilities, +/// as well as customization of command-line argument styles. +pub mod parselib { + pub use arg_picker::parselib::*; +} + +pub use arg_picker::*; + +mod entry_picker; +pub use entry_picker::*; + +mod global; +pub use global::*; diff --git a/mingling/src/picker/entry_picker.rs b/mingling/src/picker/entry_picker.rs new file mode 100644 index 0000000..7a980c6 --- /dev/null +++ b/mingling/src/picker/entry_picker.rs @@ -0,0 +1,126 @@ +use mingling_core::{ChainProcess, Groupped, ProgramCollect}; + +use crate::{picker::Pickable, picker::Picker, picker::PickerArg, picker::PickerPattern1}; + +/// Trait for converting Mingling entry types (types that implement `Groupped<R>` and `Into<Vec<String>>`) +/// into [`Picker`] instances for a given route type `R`. +/// +/// This trait provides a bridge between entry definitions created with the `mingling` framework +/// and the picker argument system used for CLI argument parsing and routing. +/// +/// # Type Parameters +/// +/// * `'a` — The lifetime of the picker and its references to argument definitions. +/// * `This` — The program type used for dispatching runtime routes; must implement [`ProgramCollect`]. +/// * `Route` — The route type used for dispatching; must implement [`Groupped<This>`]. +pub trait EntryPicker<'a, This> { + /// Converts `self` into a [`Picker`] for the given route type `Route`. + fn to_picker(self) -> Picker<'a, ChainProcess<This>>; + + /// Starts building a picker pattern with the first argument. + /// + /// Returns a [`PickerPattern1`] that can be further chained with additional + /// arguments, defaults, routes, and post-processing. + /// + /// # Type Parameters + /// + /// * `Next` — The nominal type of the first argument; must implement [`Pickable`]. + /// + /// # Parameters + /// + /// * `arg` — The argument definition, typically obtained from [`crate::macros::arg`]. + fn pick<Next>( + self, + arg: impl Into<&'a PickerArg<'a, Next>>, + ) -> PickerPattern1<'a, Next, ChainProcess<This>> + where + Self: Sized, + Next: Pickable<'a> + Default + Sized, + { + let picker = Self::to_picker(self); + Picker::build_pattern1(picker.into_args(), arg.into(), None) + } + + /// Starts building a picker pattern with the first argument, using a default value provider. + /// + /// This is a shorthand for calling `.pick(arg).or(func)`. + /// + /// # Type Parameters + /// + /// * `Next` — The nominal type of the first argument; must implement [`Pickable`]. + /// + /// # Parameters + /// + /// * `arg` — The argument definition, typically obtained from [`crate::macros::arg`]. + /// * `func` — A closure that provides a default value if the arg is not provided by the user. + fn pick_or<Next, F>( + self, + arg: impl Into<&'a PickerArg<'a, Next>>, + func: F, + ) -> PickerPattern1<'a, Next, ChainProcess<This>> + where + Self: Sized, + Next: Pickable<'a> + Default + Sized, + F: FnMut() -> Next + 'static, + { + self.pick(arg).or(func) + } + + /// Starts building a picker pattern with the first argument, using a default value. + /// + /// This is a shorthand for calling `.pick(arg).or_default()`. + /// + /// # Type Parameters + /// + /// * `Next` — The nominal type of the first argument; must implement [`Pickable`] and [`Default`]. + /// + /// # Parameters + /// + /// * `arg` — The argument definition, typically obtained from [`crate::macros::arg`]. + fn pick_or_default<Next>( + self, + arg: impl Into<&'a PickerArg<'a, Next>>, + ) -> PickerPattern1<'a, Next, ChainProcess<This>> + where + Self: Sized, + Next: Pickable<'a> + Default + Sized, + { + self.pick(arg).or_default() + } + + /// Starts building a picker pattern with the first argument, using a route if the arg is not provided. + /// + /// This is a shorthand for calling `.pick(arg).or_route(func)`. + /// + /// # Type Parameters + /// + /// * `Next` — The nominal type of the first argument; must implement [`Pickable`]. + /// + /// # Parameters + /// + /// * `arg` — The argument definition, typically obtained from [`crate::macros::arg`]. + /// * `func` — A closure that produces a route value if the arg is not provided by the user. + fn pick_or_route<Next, F>( + self, + arg: impl Into<&'a PickerArg<'a, Next>>, + func: F, + ) -> PickerPattern1<'a, Next, ChainProcess<This>> + where + Self: Sized, + Next: Pickable<'a> + Default + Sized, + F: FnMut() -> ChainProcess<This> + 'static, + { + self.pick(arg).or_route(func) + } +} + +impl<'a, This, Bind> EntryPicker<'a, This> for Bind +where + This: ProgramCollect<Enum = This>, + Bind: Groupped<This> + Into<Vec<String>>, +{ + fn to_picker(self) -> Picker<'a, ChainProcess<This>> { + let args = self.into(); + Picker::from(args) + } +} diff --git a/mingling/src/picker/global.rs b/mingling/src/picker/global.rs new file mode 100644 index 0000000..c203524 --- /dev/null +++ b/mingling/src/picker/global.rs @@ -0,0 +1,35 @@ +use arg_picker::{IntoPicker, Pickable, PickerArg, value::Flag}; +use mingling_core::{Program, ProgramCollect}; + +use crate::consts::REMAINS; + +/// Picks a global flag from the program's arguments. +/// +/// This function takes ownership of the program's current arguments, picks the specified `flag` +/// from them, and then returns the remaining arguments back to the program. It returns the +/// boolean value of the flag. +pub fn pick_global_flag<C>(program: &mut Program<C>, flag: &PickerArg<Flag>) -> bool +where + C: ProgramCollect<Enum = C>, +{ + let args = program.take_args(); + let (flag, args) = args.pick(flag).pick(&REMAINS).unwrap(); + program.replace_args(args.into()); + *flag +} + +/// Picks a global argument from the program's arguments. +/// +/// This function takes ownership of the program's current arguments, picks the specified `arg` +/// from them, and then returns the remaining arguments back to the program. It returns the +/// picked argument value, or `None` if the argument was not present. +pub fn pick_global_argument<C, A>(program: &mut Program<C>, arg: &PickerArg<A>) -> Option<A> +where + A: for<'a> Pickable<'a> + Default, + C: ProgramCollect<Enum = C>, +{ + let args = program.take_args(); + let (arg, remains) = args.pick(arg).pick(&REMAINS).unpack(); + program.replace_args(remains.unwrap().into()); + arg +} diff --git a/mingling/src/setups/picker.rs b/mingling/src/setups/picker.rs index 3c0d1c8..0b7bd33 100644 --- a/mingling/src/setups/picker.rs +++ b/mingling/src/setups/picker.rs @@ -5,6 +5,3 @@ pub use basic::*; mod structural_renderer; #[cfg(feature = "structural_renderer")] pub use structural_renderer::*; - -mod consts; -pub use consts::*; diff --git a/mingling/src/setups/picker/basic.rs b/mingling/src/setups/picker/basic.rs index 318edbd..b8cc237 100644 --- a/mingling/src/setups/picker/basic.rs +++ b/mingling/src/setups/picker/basic.rs @@ -1,27 +1,11 @@ -use arg_picker::{IntoPicker, PickerArg, value::Flag}; +use arg_picker::{PickerArg, value::Flag}; use mingling_core::{Program, ProgramCollect, setup::ProgramSetup}; use crate::{ - setup::picker::REMAINS, - setups::picker::{CONFIRM_FLAG, HELP_FLAG, QUIET_FLAG}, + consts::{CONFIRM_FLAG, HELP_FLAG, QUIET_FLAG}, + picker::pick_global_flag, }; -/// Helper: picks a boolean flag from the program arguments, calls `f` with the -/// flag value, then replaces the program arguments with the remaining args. -fn pick_flag<'a, C>( - program: &mut Program<C>, - flag: &PickerArg<'a, Flag>, - f: impl FnOnce(bool, &mut Program<C>), -) where - C: ProgramCollect<Enum = C>, -{ - let args = program.take_args(); - let remains_arg = PickerArg::<PickerArgs<'a>>::new(&[], None, true); - let (active, remains) = args.pick(flag).pick(&remains_arg).unwrap(); - f(*active, program); - program.replace_args(remains.into()); -} - /// Performs basic program initialization: /// /// - Collects `--quiet` flag to control message rendering @@ -59,9 +43,7 @@ where C: ProgramCollect<Enum = C>, { fn setup(self, program: &mut Program<C>) { - pick_flag(program, self.flag, |active, ctx| { - ctx.user_context.help = active; - }); + pick_global_flag(program, self.flag); } } @@ -90,12 +72,7 @@ where C: ProgramCollect<Enum = C>, { fn setup(self, program: &mut Program<C>) { - pick_flag(program, self.flag, |active, ctx| { - if active { - ctx.stdout_setting.render_output = false; - ctx.stdout_setting.error_output = false; - } - }); + pick_global_flag(program, self.flag); } } @@ -124,11 +101,7 @@ where C: ProgramCollect<Enum = C>, { fn setup(self, program: &mut Program<C>) { - pick_flag(program, self.flag, |active, ctx| { - if active { - ctx.user_context.confirm = true; - } - }); + pick_global_flag(program, self.flag); } } diff --git a/mingling/src/setups/picker/structural_renderer.rs b/mingling/src/setups/picker/structural_renderer.rs index 8e5eac6..1fa48fd 100644 --- a/mingling/src/setups/picker/structural_renderer.rs +++ b/mingling/src/setups/picker/structural_renderer.rs @@ -1,11 +1,8 @@ -use arg_picker::IntoPicker; use mingling_core::{Program, ProgramCollect, setup::ProgramSetup}; use crate::{ - setup::picker::REMAINS, - setups::picker::{ - JSON_FLAG, JSON_PRETTY_FLAG, RON_FLAG, RON_PRETTY_FLAG, TOML_FLAG, YAML_FLAG, - }, + consts::RENDERER_ARG, + picker::{pick_global_argument, pick_global_flag}, }; /// Sets up the structural renderer for the program: @@ -18,9 +15,9 @@ where C: ProgramCollect<Enum = C>, { fn setup(self, program: &mut Program<C>) { - program.global_argument("--renderer", |p, renderer| { - p.structural_renderer_name = renderer.into(); - }); + if let Some(renderer) = pick_global_argument(program, &RENDERER_ARG) { + program.structural_renderer_name = renderer.into(); + } } } @@ -33,6 +30,17 @@ where /// * `--toml` for TOML output /// * `--ron` for RON output /// * `--ron-pretty` for pretty-printed RON output +/// +/// # Flag priority +/// +/// If multiple flags are specified, the last matching flag in the following +/// declaration order takes precedence: +/// 1. `--json` +/// 2. `--json-pretty` +/// 3. `--yaml` +/// 4. `--toml` +/// 5. `--ron` +/// 6. `--ron-pretty` pub struct StructuralRendererSetup; impl<C> ProgramSetup<C> for StructuralRendererSetup @@ -40,50 +48,29 @@ where C: ProgramCollect<Enum = C>, { fn setup(self, program: &mut Program<C>) { - let args = program.take_args(); - let args = process_renderer_flags(args, program); - program.replace_args(args); + #[cfg(feature = "json_serde_fmt")] + if pick_global_flag(program, &crate::consts::JSON_FLAG) { + program.structural_renderer_name = crate::StructuralRendererSetting::Json; + } + #[cfg(feature = "json_serde_fmt")] + if pick_global_flag(program, &crate::consts::JSON_PRETTY_FLAG) { + program.structural_renderer_name = crate::StructuralRendererSetting::JsonPretty; + } + #[cfg(feature = "yaml_serde_fmt")] + if pick_global_flag(program, &crate::consts::YAML_FLAG) { + program.structural_renderer_name = crate::StructuralRendererSetting::Yaml; + } + #[cfg(feature = "toml_serde_fmt")] + if pick_global_flag(program, &crate::consts::TOML_FLAG) { + program.structural_renderer_name = crate::StructuralRendererSetting::Toml; + } + #[cfg(feature = "ron_serde_fmt")] + if pick_global_flag(program, &crate::consts::RON_FLAG) { + program.structural_renderer_name = crate::StructuralRendererSetting::Ron; + } + #[cfg(feature = "ron_serde_fmt")] + if pick_global_flag(program, &crate::consts::RON_PRETTY_FLAG) { + program.structural_renderer_name = crate::StructuralRendererSetting::RonPretty; + } } } - -fn process_renderer_flags<C>(args: Vec<String>, program: &mut Program<C>) -> Vec<String> -where - C: ProgramCollect<Enum = C>, -{ - let (json, json_pretty, yaml, toml, ron, ron_pretty, remains) = args - .pick(&JSON_FLAG) - .pick(&JSON_PRETTY_FLAG) - .pick(&YAML_FLAG) - .pick(&TOML_FLAG) - .pick(&RON_FLAG) - .pick(&RON_PRETTY_FLAG) - .pick(&REMAINS) - .unwrap(); - - #[cfg(feature = "json_serde_fmt")] - if *json { - program.structural_renderer_name = crate::StructuralRendererSetting::Json; - } - #[cfg(feature = "json_serde_fmt")] - if *json_pretty { - program.structural_renderer_name = crate::StructuralRendererSetting::JsonPretty; - } - #[cfg(feature = "yaml_serde_fmt")] - if *yaml { - program.structural_renderer_name = crate::StructuralRendererSetting::Yaml; - } - #[cfg(feature = "toml_serde_fmt")] - if *toml { - program.structural_renderer_name = crate::StructuralRendererSetting::Toml; - } - #[cfg(feature = "ron_serde_fmt")] - if *ron { - program.structural_renderer_name = crate::StructuralRendererSetting::Ron; - } - #[cfg(feature = "ron_serde_fmt")] - if *ron_pretty { - program.structural_renderer_name = crate::StructuralRendererSetting::RonPretty; - } - - remains.into() -} |
