aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json2
-rw-r--r--.zed/settings.json9
-rw-r--r--CHANGELOG.md23
-rw-r--r--arg_picker/src/arg.rs26
-rw-r--r--arg_picker/src/builtin.rs1
-rw-r--r--arg_picker/src/builtin/pick_picker_args.rs21
-rw-r--r--arg_picker/src/parselib.rs23
-rw-r--r--arg_picker/src/picker.rs20
-rw-r--r--arg_picker/src/picker/parse.rs8
-rw-r--r--arg_picker/src/picker/result.rs8
-rw-r--r--arg_picker/test/src/test/route_test.rs4
-rw-r--r--mingling/src/setups.rs3
-rw-r--r--mingling/src/setups/picker.rs10
-rw-r--r--mingling/src/setups/picker/basic.rs138
-rw-r--r--mingling/src/setups/picker/consts.rs105
-rw-r--r--mingling/src/setups/picker/structural_renderer.rs87
-rw-r--r--mingling_core/src/program.rs27
17 files changed, 503 insertions, 12 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0ba35e6..1b585d1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,7 +8,9 @@
"arg_picker/test/Cargo.toml",
],
"rust-analyzer.cargo.features": [
+ "structural_renderer",
"mingling_support",
+ "all_serde_fmt",
"picker",
"parser",
"comp",
diff --git a/.zed/settings.json b/.zed/settings.json
index 40eca98..727f955 100644
--- a/.zed/settings.json
+++ b/.zed/settings.json
@@ -11,7 +11,14 @@
"arg_picker/test/Cargo.toml",
],
"cargo": {
- "features": ["mingling_support", "picker", "parser", "comp"],
+ "features": [
+ "structural_renderer",
+ "mingling_support",
+ "all_serde_fmt",
+ "picker",
+ "parser",
+ "comp",
+ ],
},
},
},
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49b1f35..ea4b737 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -161,6 +161,29 @@ None
}
```
+7. **[`setups`]** Refactored `BasicProgramSetup`, `HelpFlagSetup`, `QuietFlagSetup`, `ConfirmFlagSetup`, `StructuralRendererSetup`, and `StructuralRendererSimpleSetup` into the `picker` subsystem under `mingling::setups::picker`. These setups now use the `arg_picker` (`picker`) chained argument parsing API internally instead of directly manipulating global arguments.
+
+ - The `BasicProgramSetup`, `HelpFlagSetup`, `QuietFlagSetup`, and `ConfirmFlagSetup` structs now use `PickerArg<Flag>` and chained `.pick()` calls to detect flags from the argument list, replacing the previous `global_argument`-based approach.
+ - The `StructuralRendererSetup` struct now uses `PickerArg<Flag>` constants (e.g., `JSON_FLAG`, `YAML_FLAG`) and chained `.pick()` calls to detect format-specifying flags, replacing the previous `global_argument` approach.
+ - The `StructuralRendererSimpleSetup` struct still uses the legacy `global_argument("--renderer", ...)` approach, preserving backward compatibility with the `--renderer <FORMAT>` syntax.
+ - New `PickerArg<Flag>` constants have been added in `mingling::setups::picker::consts`: `HELP_FLAG`, `QUIET_FLAG`, `CONFIRM_FLAG`, `JSON_FLAG`, `JSON_PRETTY_FLAG`, `YAML_FLAG`, `TOML_FLAG`, `RON_FLAG`, and `RON_PRETTY_FLAG`. The format-specific flags are feature-gated behind their respective `json_serde_fmt`, `yaml_serde_fmt`, `toml_serde_fmt`, and `ron_serde_fmt` features.
+ - The module structure is:
+ - `mingling::setups::picker` — re-exports all picker-based setup types
+ - `mingling::setups::picker::basic` — `BasicProgramSetup`, `HelpFlagSetup`, `QuietFlagSetup`, `ConfirmFlagSetup`
+ - `mingling::setups::picker::consts` — reusable `PickerArg<Flag>` constants
+ - `mingling::setups::picker::structural_renderer` — `StructuralRendererSetup`, `StructuralRendererSimpleSetup`
+ - All setup types remain available from `mingling::setups::*` as before — this is purely an internal refactoring; no public API surface changes.
+
+ The `picker` feature must be enabled for these refactored setups to be available. When the feature is disabled, the original implementations (using `global_argument`) remain in effect.
+
+8. **[`core`]** Added `get_args_mut()`, `take_args()`, and `replace_args()` methods to `Program` for more flexible argument manipulation:
+
+ - **`get_args_mut(&mut self) -> &mut [String]`** — Returns a mutable reference to the program's command-line arguments, allowing in-place modification of individual arguments.
+ - **`take_args(&mut self) -> Vec<String>`** — Takes ownership of the program's command-line arguments, replacing them with an empty `Vec`. Useful for transferring arguments to another context or processing them with ownership.
+ - **`replace_args(&mut self, args: Vec<String>) -> Vec<String>`** — Replaces the program's command-line arguments with a new set and returns the old ones. Enables swapping argument sets during program execution.
+
+ These methods complement the existing read-only `get_args(&self)` method, providing full control over argument mutation and ownership.
+
#### **BREAKING CHANGES** (API CHANGES):
1. **[`macros:renderer`]** **[`macros:help`]** Removed `r_println!` and `r_print!` macros. The `#[renderer]` and `#[help]` macros no longer implicitly inject an internal `RenderResult` variable or provide `r_println!` / `r_print!` macros.
diff --git a/arg_picker/src/arg.rs b/arg_picker/src/arg.rs
index 78ad539..a352418 100644
--- a/arg_picker/src/arg.rs
+++ b/arg_picker/src/arg.rs
@@ -138,9 +138,27 @@ where
/// Describes the attribute (behavior) of a command-line parameter.
///
/// The ordering reflects parse priority (higher = parsed first):
-/// `PositionalMulti < Positional < Flag < Single < Multi`
+/// `Postprocess < Final < PositionalMulti < Positional < Flag < Single < Multi < Begin < Preprocess`
+///
+/// # Variants
+///
+/// - `Postprocess` — Reserved lowest priority, used only in special cases.
+/// - `Final` — Reserved post-processing priority, used only in special cases.
+/// - `PositionalMulti` — Positional argument that accepts multiple values (e.g., multiple input files).
+/// - `Positional` — Positional argument matched by its position (e.g., an input file).
+/// - `Flag` — Boolean flag with no associated value (e.g., `--verbose`).
+/// - `Single` — Accepts a single value (e.g., `--name Alice`).
+/// - `Multi` — Accepts multiple values (e.g., `--file a.txt --file b.txt`).
+/// - `Begin` — Reserved pre-processing priority, used only in special cases.
+/// - `Preprocess` — Reserved highest priority, used only in special cases.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum PickerArgAttr {
+ /// Reserved lowest priority, used only in special cases.
+ Postprocess,
+
+ /// Reserved post-processing priority, used only in special cases.
+ Final,
+
/// Positional argument that accepts multiple values (e.g., multiple input files).
PositionalMulti,
@@ -156,6 +174,12 @@ pub enum PickerArgAttr {
/// Accepts multiple values (e.g., `--file a.txt --file b.txt`).
Multi,
+
+ /// Reserved pre-processing priority, used only in special cases.
+ Begin,
+
+ /// Reserved highest priority, used only in special cases.
+ Preprocess,
}
impl PickerArgAttr {
diff --git a/arg_picker/src/builtin.rs b/arg_picker/src/builtin.rs
index e855b08..1c698ba 100644
--- a/arg_picker/src/builtin.rs
+++ b/arg_picker/src/builtin.rs
@@ -1,4 +1,5 @@
mod pick_bool;
mod pick_flag;
mod pick_numbers;
+mod pick_picker_args;
mod pick_string;
diff --git a/arg_picker/src/builtin/pick_picker_args.rs b/arg_picker/src/builtin/pick_picker_args.rs
new file mode 100644
index 0000000..419cbc8
--- /dev/null
+++ b/arg_picker/src/builtin/pick_picker_args.rs
@@ -0,0 +1,21 @@
+use crate::{PickerArgResult::Parsed, PickerArgs, parselib::build_masked_args, pickable_needed::*};
+
+impl<'a> Pickable<'a> for PickerArgs<'a> {
+ fn get_attr(_flag: &'a PickerArg<'a, Self>) -> PickerArgAttr {
+ // Use the lowest priority attribute
+ PickerArgAttr::Postprocess
+ }
+
+ fn tag(ctx: TagPhaseContext) -> Vec<usize> {
+ // Collect all remaining raw index values
+ build_masked_args(ctx.args, ctx.mask)
+ .iter()
+ .map(|m| m.raw_idx)
+ .collect()
+ }
+
+ fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
+ let remains: Vec<String> = raw_strs.iter().map(|s| s.to_string()).collect();
+ Parsed(PickerArgs::Owned(remains))
+ }
+}
diff --git a/arg_picker/src/parselib.rs b/arg_picker/src/parselib.rs
index 7fbd606..0fcd583 100644
--- a/arg_picker/src/parselib.rs
+++ b/arg_picker/src/parselib.rs
@@ -117,13 +117,32 @@ impl<'a> From<crate::TagPhaseContext<'a>> for MatcherContext<'a> {
}
}
+/// Checks whether the argument at index `idx` is already claimed (masked).
+///
+/// Returns `true` if `idx` is within the mask bounds and the mask value is non-zero,
+/// indicating the argument has been claimed by a previous matcher.
+///
+/// # Arguments
+///
+/// * `mask` - A byte slice where non-zero values indicate claimed arguments.
+/// * `idx` - The index to check in the mask.
#[inline(always)]
-fn is_masked(mask: &[u8], idx: usize) -> bool {
+pub fn is_masked(mask: &[u8], idx: usize) -> bool {
idx < mask.len() && mask[idx] != 0
}
+/// Builds a vector of [`MaskedArg`] from the given `PickerArgs` and mask.
+///
+/// Only arguments whose mask entry is `0` (i.e., available/not yet claimed) are included.
+/// Each resulting [`MaskedArg`] retains its original raw string and its index in the full
+/// argument list for later reference.
+///
+/// # Arguments
+///
+/// * `args` - The full set of parsed arguments.
+/// * `mask` - A byte slice where `0` means available and non-zero means already claimed.
#[inline(always)]
-fn build_masked_args<'a>(args: &'a PickerArgs, mask: &'a [u8]) -> Vec<MaskedArg<'a>> {
+pub fn build_masked_args<'a>(args: &'a PickerArgs, mask: &'a [u8]) -> Vec<MaskedArg<'a>> {
let mut cidx = 0;
args.iter()
.filter_map(|r| {
diff --git a/arg_picker/src/picker.rs b/arg_picker/src/picker.rs
index ecab648..7a60e16 100644
--- a/arg_picker/src/picker.rs
+++ b/arg_picker/src/picker.rs
@@ -73,6 +73,26 @@ pub enum PickerArgs<'a> {
Owned(Vec<String>),
}
+impl<'a> From<PickerArgs<'a>> for Vec<String> {
+ fn from(value: PickerArgs<'a>) -> Self {
+ match value {
+ PickerArgs::Slice(items) => items.iter().map(|s| s.to_string()).collect(),
+ PickerArgs::Vec(items) => items.into_iter().map(|s| s.to_string()).collect(),
+ PickerArgs::Owned(items) => items,
+ }
+ }
+}
+
+impl<'a> From<&'a PickerArgs<'a>> for Vec<&'a str> {
+ fn from(value: &'a PickerArgs<'a>) -> Self {
+ match value {
+ PickerArgs::Slice(items) => items.to_vec(),
+ PickerArgs::Vec(items) => items.clone(),
+ PickerArgs::Owned(items) => items.iter().map(|s| s.as_str()).collect(),
+ }
+ }
+}
+
impl<'a> Default for PickerArgs<'a> {
fn default() -> Self {
Self::Vec(vec![])
diff --git a/arg_picker/src/picker/parse.rs b/arg_picker/src/picker/parse.rs
index 366028b..9db5bd9 100644
--- a/arg_picker/src/picker/parse.rs
+++ b/arg_picker/src/picker/parse.rs
@@ -21,14 +21,16 @@ internal_repeat!(1..=32 => {
internal_repeat!(1..=32 => {
impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
where (T$: Pickable<'a>,+) {
- /// Unwraps the result, panicking if a route was selected.
+ /// Unwraps the result, panicking if a route was selected or a required
+ /// value is missing.
///
/// # Panics
///
- /// Panics if a route was selected.
+ /// Panics if a route was selected, or if a required argument was not
+ /// provided by the user.
pub fn unwrap(self) -> ((T$,+)) {
let p = self.parse();
- ((p.v$.unwrap(),+))
+ ((p.v$.expect(concat!("missing required argument at position ", $)),+))
}
/// Returns the individual option values without checking the route.
diff --git a/arg_picker/src/picker/result.rs b/arg_picker/src/picker/result.rs
index 83ca2cd..2099825 100644
--- a/arg_picker/src/picker/result.rs
+++ b/arg_picker/src/picker/result.rs
@@ -21,13 +21,15 @@ internal_repeat!(1..=32 => {
internal_repeat!(1..=32 => {
impl<(T$,+), Route> PickerResult$<(T$,+), Route> {
- /// Unwraps the result, panicking if a route was selected.
+ /// Unwraps the result, panicking if a route was selected or a required
+ /// value is missing.
///
/// # Panics
///
- /// Panics if `self.route` is `Some(...)`.
+ /// Panics if `self.route` is `Some(...)`, or if a required argument was
+ /// not provided by the user.
pub fn unwrap(self) -> ((T$,+)) {
- ((self.v$.unwrap(),+))
+ ((self.v$.expect(concat!("missing required argument at position ", $)),+))
}
/// Returns the individual option values without checking the route.
diff --git a/arg_picker/test/src/test/route_test.rs b/arg_picker/test/src/test/route_test.rs
index 7594c6e..c9cd5ab 100644
--- a/arg_picker/test/src/test/route_test.rs
+++ b/arg_picker/test/src/test/route_test.rs
@@ -1,4 +1,4 @@
-use arg_picker::{macros::arg, IntoPicker};
+use arg_picker::{IntoPicker, macros::arg};
// Route mechanism — or_route
@@ -53,7 +53,7 @@ fn test_or_route_to_option_returns_none() {
}
#[test]
-#[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
+#[should_panic(expected = "missing required argument")]
fn test_or_route_unwrap_panics() {
let args: Vec<&str> = vec![];
args.with_route::<&'static str>()
diff --git a/mingling/src/setups.rs b/mingling/src/setups.rs
index e572cf5..a1290ff 100644
--- a/mingling/src/setups.rs
+++ b/mingling/src/setups.rs
@@ -7,6 +7,9 @@ pub use dirs::*;
mod exit_code;
pub use exit_code::*;
+#[cfg(feature = "picker")]
+pub mod picker;
+
#[cfg(feature = "structural_renderer")]
mod structural_renderer;
diff --git a/mingling/src/setups/picker.rs b/mingling/src/setups/picker.rs
new file mode 100644
index 0000000..3c0d1c8
--- /dev/null
+++ b/mingling/src/setups/picker.rs
@@ -0,0 +1,10 @@
+mod basic;
+pub use basic::*;
+
+#[cfg(feature = "structural_renderer")]
+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
new file mode 100644
index 0000000..0539a09
--- /dev/null
+++ b/mingling/src/setups/picker/basic.rs
@@ -0,0 +1,138 @@
+use arg_picker::{IntoPicker, PickerArg, PickerArgs, value::Flag};
+use mingling_core::{Program, ProgramCollect, setup::ProgramSetup};
+
+use crate::setups::picker::{CONFIRM_FLAG, HELP_FLAG, QUIET_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
+/// - Collects `--help` flag to enable help mode
+/// - Collects `--confirm` flag to skip user confirmation
+pub struct BasicProgramSetup;
+
+impl<C> ProgramSetup<C> for BasicProgramSetup
+where
+ C: ProgramCollect<Enum = C>,
+{
+ fn setup(self, program: &mut Program<C>) {
+ program.with_setup(HelpFlagSetup::default());
+ program.with_setup(QuietFlagSetup::default());
+ program.with_setup(ConfirmFlagSetup::default());
+ }
+}
+
+/// Provides setup for parsing the user help flag
+///
+/// The default value is `--help / -h`
+pub struct HelpFlagSetup<'a> {
+ flag: &'a PickerArg<'a, Flag>,
+}
+
+impl<'a> HelpFlagSetup<'a> {
+ /// Creates a new `HelpFlagSetup` with the given flag aliases.
+ pub fn new(flag: &'a PickerArg<Flag>) -> Self {
+ Self { flag }
+ }
+}
+
+impl<'a, C> ProgramSetup<C> for HelpFlagSetup<'a>
+where
+ C: ProgramCollect<Enum = C>,
+{
+ fn setup(self, program: &mut Program<C>) {
+ pick_flag(program, self.flag, |active, ctx| {
+ ctx.user_context.help = active;
+ });
+ }
+}
+
+impl<'a> Default for HelpFlagSetup<'a> {
+ fn default() -> Self {
+ Self { flag: &HELP_FLAG }
+ }
+}
+
+/// Provides setup for parsing the quiet flag
+///
+/// The default value is `--quiet / -q`
+pub struct QuietFlagSetup<'a> {
+ flag: &'a PickerArg<'a, Flag>,
+}
+
+impl<'a> QuietFlagSetup<'a> {
+ /// Creates a new `QuietFlagSetup` with the given flag aliases.
+ pub fn new(flag: &'a PickerArg<Flag>) -> Self {
+ Self { flag }
+ }
+}
+
+impl<'a, C> ProgramSetup<C> for QuietFlagSetup<'a>
+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;
+ }
+ });
+ }
+}
+
+impl<'a> Default for QuietFlagSetup<'a> {
+ fn default() -> Self {
+ Self { flag: &QUIET_FLAG }
+ }
+}
+
+/// Provides setup for parsing the confirm flag
+///
+/// The default value is `--confirm / -C`
+pub struct ConfirmFlagSetup<'a> {
+ flag: &'a PickerArg<'a, Flag>,
+}
+
+impl<'a> ConfirmFlagSetup<'a> {
+ /// Creates a new `ConfirmFlagSetup` with the given flag aliases.
+ pub fn new(flag: &'a PickerArg<Flag>) -> Self {
+ Self { flag }
+ }
+}
+
+impl<'a, C> ProgramSetup<C> for ConfirmFlagSetup<'a>
+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;
+ }
+ });
+ }
+}
+
+impl<'a> Default for ConfirmFlagSetup<'a> {
+ fn default() -> Self {
+ Self {
+ flag: &CONFIRM_FLAG,
+ }
+ }
+}
diff --git a/mingling/src/setups/picker/consts.rs b/mingling/src/setups/picker/consts.rs
new file mode 100644
index 0000000..e254b4f
--- /dev/null
+++ b/mingling/src/setups/picker/consts.rs
@@ -0,0 +1,105 @@
+use std::marker::PhantomData;
+
+use arg_picker::{PickerArg, value::Flag};
+
+/// Help flag: display usage information.
+/// - `full`: `["help"]`
+/// - `short`: `'h'`
+pub const HELP_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["help"],
+ short: Some('h'),
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// Quiet flag: suppress output.
+/// - `full`: `["quiet"]`
+/// - `short`: `'q'`
+pub const QUIET_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["quiet"],
+ short: Some('q'),
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// Confirm flag: require user confirmation before proceeding.
+/// - `full`: `["confirm"]`
+/// - `short`: `'C'`
+pub const CONFIRM_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["confirm"],
+ short: Some('C'),
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// JSON flag: enable JSON output format.
+/// - `full`: `["json"]`
+/// - `short`: (none)
+/// Available only when the `json_serde_fmt` feature is enabled.
+#[cfg(feature = "json_serde_fmt")]
+pub const JSON_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["json"],
+ short: None,
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// JSON pretty flag: enable pretty-printed JSON output format.
+/// - `full`: `["json_pretty"]`
+/// - `short`: (none)
+/// Available only when the `json_serde_fmt` feature is enabled.
+#[cfg(feature = "json_serde_fmt")]
+pub const JSON_PRETTY_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["json_pretty"],
+ short: None,
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// YAML flag: enable YAML output format.
+/// - `full`: `["yaml"]`
+/// - `short`: (none)
+/// Available only when the `yaml_serde_fmt` feature is enabled.
+#[cfg(feature = "yaml_serde_fmt")]
+pub const YAML_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["yaml"],
+ short: None,
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// TOML flag: enable TOML output format.
+/// - `full`: `["toml"]`
+/// - `short`: (none)
+/// Available only when the `toml_serde_fmt` feature is enabled.
+#[cfg(feature = "toml_serde_fmt")]
+pub const TOML_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["toml"],
+ short: None,
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// RON flag: enable RON output format.
+/// - `full`: `["ron"]`
+/// - `short`: (none)
+/// Available only when the `ron_serde_fmt` feature is enabled.
+#[cfg(feature = "ron_serde_fmt")]
+pub const RON_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["ron"],
+ short: None,
+ positional: false,
+ internal_type: PhantomData,
+};
+
+/// RON pretty flag: enable pretty-printed RON output format.
+/// - `full`: `["ron_pretty"]`
+/// - `short`: (none)
+/// Available only when the `ron_serde_fmt` feature is enabled.
+#[cfg(feature = "ron_serde_fmt")]
+pub const RON_PRETTY_FLAG: PickerArg<Flag> = PickerArg::<Flag> {
+ full: &["ron_pretty"],
+ short: None,
+ positional: false,
+ internal_type: PhantomData,
+};
diff --git a/mingling/src/setups/picker/structural_renderer.rs b/mingling/src/setups/picker/structural_renderer.rs
new file mode 100644
index 0000000..69f05f1
--- /dev/null
+++ b/mingling/src/setups/picker/structural_renderer.rs
@@ -0,0 +1,87 @@
+use arg_picker::{IntoPicker, PickerArg, PickerArgs};
+use mingling_core::{Program, ProgramCollect, setup::ProgramSetup};
+
+use crate::setups::picker::{
+ JSON_FLAG, JSON_PRETTY_FLAG, RON_FLAG, RON_PRETTY_FLAG, TOML_FLAG, YAML_FLAG,
+};
+
+/// Sets up the structural renderer for the program:
+///
+/// - Adds a `--renderer` global argument to specify the renderer type
+pub struct StructuralRendererSimpleSetup;
+
+impl<C> ProgramSetup<C> for StructuralRendererSimpleSetup
+where
+ C: ProgramCollect<Enum = C>,
+{
+ fn setup(self, program: &mut Program<C>) {
+ program.global_argument("--renderer", |p, renderer| {
+ p.structural_renderer_name = renderer.into();
+ });
+ }
+}
+
+/// Sets up the structural renderer for the program:
+///
+/// - Adds global flags to specify the renderer type:
+/// * `--json` for JSON output
+/// * `--json-pretty` for pretty-printed JSON output
+/// * `--yaml` for YAML output
+/// * `--toml` for TOML output
+/// * `--ron` for RON output
+/// * `--ron-pretty` for pretty-printed RON output
+pub struct StructuralRendererSetup;
+
+impl<C> ProgramSetup<C> for StructuralRendererSetup
+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);
+ }
+}
+
+fn process_renderer_flags<C>(args: Vec<String>, program: &mut Program<C>) -> Vec<String>
+where
+ C: ProgramCollect<Enum = C>,
+{
+ let remains_arg = PickerArg::<PickerArgs>::new(&[], None, true);
+ 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_arg)
+ .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()
+}
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index 71d5290..0d409b7 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -130,6 +130,33 @@ where
&self.args
}
+ /// Returns a mutable reference to the program's command-line arguments.
+ #[must_use]
+ pub fn get_args_mut(&mut self) -> &mut [String] {
+ &mut self.args
+ }
+
+ /// Takes ownership of the program's command-line arguments, replacing them with an empty Vec.
+ /// This is useful when you need to transfer the arguments to another context or process them
+ /// and then replace them later.
+ #[must_use]
+ pub fn take_args(&mut self) -> Vec<String> {
+ std::mem::take(&mut self.args)
+ }
+
+ /// Replaces the program's command-line arguments with a new set and returns the old ones.
+ ///
+ /// # Arguments
+ ///
+ /// * `args` - The new command-line arguments to set.
+ ///
+ /// # Returns
+ ///
+ /// The previous command-line arguments.
+ pub fn replace_args(&mut self, args: Vec<String>) -> Vec<String> {
+ std::mem::replace(&mut self.args, args)
+ }
+
/// Get all registered dispatcher names from the program
#[must_use]
pub fn get_nodes(