diff options
33 files changed, 899 insertions, 37 deletions
diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/mingling.iml b/.idea/mingling.iml new file mode 100644 index 0000000..ca57e6c --- /dev/null +++ b/.idea/mingling.iml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="EMPTY_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/arg_picker/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/arg_picker_macros/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mingling/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mingling_core/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mingling_core/tests" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/mingling_macros/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mingling_pathf/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mling/src" isTestSource="false" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..764f612 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/mingling.iml" filepath="$PROJECT_DIR$/.idea/mingling.iml" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/.idea/runConfigurations/Check__All.xml b/.idea/runConfigurations/Check__All.xml new file mode 100644 index 0000000..95af540 --- /dev/null +++ b/.idea/runConfigurations/Check__All.xml @@ -0,0 +1,26 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Check: All" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="CI" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --bin ci --quiet -- -y" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2" /> + </configuration> +</component> diff --git a/.idea/runConfigurations/Check__Codes.xml b/.idea/runConfigurations/Check__Codes.xml new file mode 100644 index 0000000..f117f8f --- /dev/null +++ b/.idea/runConfigurations/Check__Codes.xml @@ -0,0 +1,26 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Check: Codes" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="CI" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --bin ci --quiet -- --test-codes -y" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2" /> + </configuration> +</component> diff --git a/.idea/runConfigurations/Check__Documents.xml b/.idea/runConfigurations/Check__Documents.xml new file mode 100644 index 0000000..b5a503b --- /dev/null +++ b/.idea/runConfigurations/Check__Documents.xml @@ -0,0 +1,26 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Check: Documents" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="CI" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --bin ci --quiet -- --test-docs -y" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2" /> + </configuration> +</component> diff --git a/.idea/runConfigurations/Refresh_All.xml b/.idea/runConfigurations/Refresh_All.xml new file mode 100644 index 0000000..24be845 --- /dev/null +++ b/.idea/runConfigurations/Refresh_All.xml @@ -0,0 +1,27 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Refresh All" + type="CompoundRunConfigurationType" + > + <toRun name="Run: Cargo Format" type="CargoCommandRunConfiguration" /> + <toRun + name="Run: Fix Documents Codebox" + type="CargoCommandRunConfiguration" + /> + <toRun + name="Run: Generate Docsify Sidebar" + type="CargoCommandRunConfiguration" + /> + <toRun + name="Run: Sync Examples To Crates" + type="CargoCommandRunConfiguration" + /> + <toRun + name="Run: Sync Examples To Helpdoc" + type="CargoCommandRunConfiguration" + /> + <toRun name="Run: Sync Feature List" type="CargoCommandRunConfiguration" /> + <method v="2" /> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Cargo_Format.xml b/.idea/runConfigurations/Run__Cargo_Format.xml new file mode 100644 index 0000000..a99a91d --- /dev/null +++ b/.idea/runConfigurations/Run__Cargo_Format.xml @@ -0,0 +1,23 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Cargo Format" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option name="command" value="fmt" /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2" /> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Deploy_API_Docs.xml b/.idea/runConfigurations/Run__Deploy_API_Docs.xml new file mode 100644 index 0000000..1a32a64 --- /dev/null +++ b/.idea/runConfigurations/Run__Deploy_API_Docs.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Deploy API Docs" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin deploy-api-docs" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Fix_Documents_Codebox.xml b/.idea/runConfigurations/Run__Fix_Documents_Codebox.xml new file mode 100644 index 0000000..0863ef1 --- /dev/null +++ b/.idea/runConfigurations/Run__Fix_Documents_Codebox.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Fix Documents Codebox" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin docs-code-box-fix" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Generate_Docsify_Sidebar.xml b/.idea/runConfigurations/Run__Generate_Docsify_Sidebar.xml new file mode 100644 index 0000000..a50a2dd --- /dev/null +++ b/.idea/runConfigurations/Run__Generate_Docsify_Sidebar.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Generate Docsify Sidebar" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin docsify-sidebar-gen" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Package_All_Crates.xml b/.idea/runConfigurations/Run__Package_All_Crates.xml new file mode 100644 index 0000000..336c9b3 --- /dev/null +++ b/.idea/runConfigurations/Run__Package_All_Crates.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Package All Crates" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin package-all" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Sync_Examples_To_Crates.xml b/.idea/runConfigurations/Run__Sync_Examples_To_Crates.xml new file mode 100644 index 0000000..43db9bb --- /dev/null +++ b/.idea/runConfigurations/Run__Sync_Examples_To_Crates.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Sync Examples To Crates" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin refresh-docs" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Sync_Examples_To_Helpdoc.xml b/.idea/runConfigurations/Run__Sync_Examples_To_Helpdoc.xml new file mode 100644 index 0000000..8e7faff --- /dev/null +++ b/.idea/runConfigurations/Run__Sync_Examples_To_Helpdoc.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Sync Examples To Helpdoc" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin sync-examples" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/runConfigurations/Run__Sync_Feature_List.xml b/.idea/runConfigurations/Run__Sync_Feature_List.xml new file mode 100644 index 0000000..1e6ed8f --- /dev/null +++ b/.idea/runConfigurations/Run__Sync_Feature_List.xml @@ -0,0 +1,28 @@ +<component name="ProjectRunConfigurationManager"> + <configuration + default="false" + name="Run: Sync Feature List" + type="CargoCommandRunConfiguration" + factoryName="Cargo Command" + folderName="Tools" + > + <option + name="command" + value="run --manifest-path .run/Cargo.toml --quiet --bin refresh-feature-mod" + /> + <option name="workingDirectory" value="file://$PROJECT_DIR$" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project>
\ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 389a476..1b585d1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,18 @@ { - "rust-analyzer.check.command": "clippy", - "rust-analyzer.checkOnSave": true, - "rust-analyzer.linkedProjects": [ - ".run/Cargo.toml", - "mingling_pathf/test/Cargo.toml", - "arg_picker/Cargo.toml", - "arg_picker/test/Cargo.toml" - ], - "rust-analyzer.cargo.features": ["mingling_support"] + "rust-analyzer.check.command": "clippy", + "rust-analyzer.checkOnSave": true, + "rust-analyzer.linkedProjects": [ + ".run/Cargo.toml", + "mingling_pathf/test/Cargo.toml", + "arg_picker/Cargo.toml", + "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 103cc28..727f955 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -1,19 +1,26 @@ { - "lsp": { - "rust-analyzer": { - "initialization_options": { - "checkOnSave": true, - "check": { "command": "clippy" }, - "linkedProjects": [ - ".run/Cargo.toml", - "mingling_pathf/test/Cargo.toml", - "arg_picker/Cargo.toml", - "arg_picker/test/Cargo.toml" - ], - "cargo": { - "features": ["mingling_support"] - } - } - } - } + "lsp": { + "rust-analyzer": { + "initialization_options": { + "checkOnSave": true, + "check": { "command": "clippy" }, + "linkedProjects": [ + ".run/Cargo.toml", + "mingling_pathf/test/Cargo.toml", + "arg_picker/Cargo.toml", + "arg_picker/test/Cargo.toml", + ], + "cargo": { + "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( |
