aboutsummaryrefslogtreecommitdiff
path: root/mingling/src/setups
diff options
context:
space:
mode:
Diffstat (limited to 'mingling/src/setups')
-rw-r--r--mingling/src/setups/confirmer.rs60
-rw-r--r--mingling/src/setups/osc94.rs89
-rw-r--r--mingling/src/setups/stdin_args.rs82
3 files changed, 231 insertions, 0 deletions
diff --git a/mingling/src/setups/confirmer.rs b/mingling/src/setups/confirmer.rs
new file mode 100644
index 0000000..1824745
--- /dev/null
+++ b/mingling/src/setups/confirmer.rs
@@ -0,0 +1,60 @@
+use mingling_core::{
+ Program, ProgramCollect, config, hook::ProgramHook, setup::ProgramSetup, this,
+};
+
+use crate::res::Confirmer;
+
+/// Confirmer setup for managing confirmation state
+///
+/// This Setup manages the confirmation flag within the program's resource
+/// store. It registers a [`Confirmer`] resource and sets up a hook that
+/// checks the user's confirmation mode during program execution.
+///
+/// # Usage
+///
+/// This Setup can be registered using the
+/// [`Program`](https://docs.rs/mingling/latest/mingling/struct.Program.html)
+/// `with_setup` method, for example:
+///
+/// ```rust
+/// # use mingling::MockProgramCollect as ThisProgram;
+/// use mingling::Program;
+/// use mingling::setup::ConfirmerSetup;
+///
+/// let mut program = Program::<ThisProgram>::new();
+/// program.with_setup(ConfirmerSetup);
+/// ```
+///
+/// # Behavior
+///
+/// - Registers a [`Confirmer`] resource that tracks confirmation state.
+/// - At the beginning of command execution, checks whether the user's
+/// confirmation mode is set to `Skip`.
+/// - If confirmation is skipped, the [`Confirmer`] resource is updated
+/// to record the confirmed state.
+///
+/// # Notes
+///
+/// - This Setup applies uniformly to all subcommands of the entire program.
+/// - The confirmation state is determined by the global `config` setting;
+/// it does not support per-command overrides.
+pub struct ConfirmerSetup;
+
+impl<C> ProgramSetup<C> for ConfirmerSetup
+where
+ C: ProgramCollect<Enum = C> + 'static,
+{
+ fn setup(self, program: &mut Program<C>) {
+ program.with_resource(Confirmer::new());
+
+ program.with_hook(ProgramHook::empty().on_pre_dispatch::<_, ()>(|_| {
+ let p = this::<C>();
+ let confirmed = p.user_context.confirmation == config::ConfirmationMode::Skip;
+ if confirmed {
+ p.modify_res(|c: &mut Confirmer| {
+ c.set_confirmed();
+ });
+ }
+ }));
+ }
+}
diff --git a/mingling/src/setups/osc94.rs b/mingling/src/setups/osc94.rs
new file mode 100644
index 0000000..2f9a319
--- /dev/null
+++ b/mingling/src/setups/osc94.rs
@@ -0,0 +1,89 @@
+use mingling_core::{Program, ProgramCollect, setup::ProgramSetup};
+
+use crate::res::OSC94;
+
+/// `OSC 9;4` Setup for managing terminal progress notification state
+///
+/// This Setup manages the terminal's `OSC 9;4` protocol support state within the
+/// program's resource store. It registers an [`OSC94`] resource that tracks whether
+/// the current terminal supports the protocol, and provides a helper resource that
+/// can be used to send progress notification messages.
+///
+/// # Usage
+///
+/// This Setup can be registered using the
+/// [`Program`](https://docs.rs/mingling/latest/mingling/struct.Program.html)
+/// `with_setup` method, for example:
+///
+/// ```rust
+/// # use mingling::MockProgramCollect as ThisProgram;
+/// use mingling::Program;
+/// use mingling::setup::OSC94Setup;
+///
+/// let mut program = Program::<ThisProgram>::new();
+/// program.with_setup(OSC94Setup);
+/// ```
+///
+/// # Behavior
+///
+/// - Registers an [`OSC94`] resource that tracks whether the current terminal
+/// supports the `OSC 9;4` protocol.
+/// - The support check inspects various environment variables such as `TERM_PROGRAM`,
+/// `WT_SESSION`, `VTE_VERSION`, and `TERM`.
+///
+/// # Notes
+///
+/// - The support state is determined at setup time and stored in the resource store.
+/// - Use [`OSC94Message`] to construct and send progress notification messages.
+pub struct OSC94Setup;
+
+impl<C> ProgramSetup<C> for OSC94Setup
+where
+ C: ProgramCollect<Enum = C> + 'static,
+{
+ fn setup(self, program: &mut Program<C>) {
+ program.with_resource(OSC94 {
+ is_support: is_support_osc94(),
+ });
+ }
+}
+
+/// Check whether the current terminal environment supports the `OSC 9;4` protocol
+///
+/// This function inspects various environment variables to determine whether the
+/// current terminal supports Microsoft's
+/// [OSC 9;4 protocol](https://learn.microsoft.com/en-us/windows/terminal/tutorials/progress-bar-sequences),
+/// which allows sending task progress notifications via ANSI escape sequences.
+///
+/// Supported terminal environments include:
+/// - **`TERM_PROGRAM`**: `ghostty`, `WezTerm`, `iTerm.app`
+/// - **`WT_SESSION`**: Windows Terminal
+/// - **`VTE_VERSION`**: VTE-based terminals (such as GNOME Terminal, Konsole, etc.)
+/// - **`TERM`**: terminal emulators containing `xterm`
+///
+/// Returns `true` if the current terminal supports the `OSC 9;4` protocol, so that
+/// progress notification escape sequences can be safely sent.
+fn is_support_osc94() -> bool {
+ if let Ok(program) = std::env::var("TERM_PROGRAM") {
+ match program.as_str() {
+ "ghostty" | "WezTerm" | "iTerm.app" => return true,
+ _ => {}
+ }
+ }
+
+ if std::env::var("WT_SESSION").is_ok() {
+ return true;
+ }
+
+ if std::env::var("VTE_VERSION").is_ok() {
+ return true;
+ }
+
+ if let Ok(term) = std::env::var("TERM")
+ && term.contains("xterm")
+ {
+ return true;
+ }
+
+ false
+}
diff --git a/mingling/src/setups/stdin_args.rs b/mingling/src/setups/stdin_args.rs
new file mode 100644
index 0000000..ca55ca7
--- /dev/null
+++ b/mingling/src/setups/stdin_args.rs
@@ -0,0 +1,82 @@
+use std::io::{IsTerminal, Read};
+
+use mingling_core::{
+ Program, ProgramCollect, hook::ProgramHook, setup::ProgramSetup, utils::ArgumentSplitter,
+};
+
+/// Uses the standard input as arguments for the program
+///
+/// This Setup can take standard input supplied via a pipe or redirect,
+/// split it according to whitespace and quoting rules, and append
+/// the resulting arguments to the end of the command argument list.
+///
+/// # Usage
+///
+/// This Setup can be registered using the
+/// [`Program`](https://docs.rs/mingling/latest/mingling/struct.Program.html)
+/// `with_setup` method, for example:
+///
+/// ```rust
+/// # use mingling::MockProgramCollect as ThisProgram;
+/// use mingling::Program;
+/// use mingling::setup::StandardInputArgsSetup;
+///
+/// let mut program = Program::<ThisProgram>::new();
+/// program.with_setup(StandardInputArgsSetup);
+/// ```
+///
+/// # Behavior
+///
+/// - Standard input is only read when it is not a terminal (i.e., when
+/// there is piped or redirected input).
+/// - The read content is split into multiple arguments according to
+/// whitespace and quoting rules.
+/// - If the standard input content is empty, no arguments are produced.
+/// - All input is converted to UTF-8 encoding (lossy conversion is used
+/// when strict parsing is not possible).
+///
+/// # Notes
+///
+/// - This Setup applies uniformly to all subcommands of the entire program
+/// and does not provide fine-grained control. If you need different
+/// standard input behavior across different subcommands (e.g., some
+/// subcommands read stdin while others ignore it), **do not use this Setup**.
+/// - This Setup does **not** provide any validation rules. Content provided
+/// via standard input is treated as trusted arguments and appended directly.
+/// As a result, the input source can also inject arbitrary arguments into
+/// the command, so you should be careful when processing untrusted input.
+pub struct StandardInputArgsSetup;
+
+impl<C> ProgramSetup<C> for StandardInputArgsSetup
+where
+ C: ProgramCollect<Enum = C>,
+{
+ fn setup(self, program: &mut Program<C>) {
+ program.with_hook(ProgramHook::empty().on_pre_dispatch(|ctx| {
+ let pipe_input = read_stdin();
+ if let Some(pipe_input) = pipe_input {
+ ctx.arguments.append(&mut pipe_input.trim().split_args());
+ }
+ }));
+ }
+}
+
+fn read_stdin() -> Option<String> {
+ // Check if stdin is a terminal (no piped input) or has data available
+ if std::io::stdin().is_terminal() {
+ return None;
+ }
+
+ let mut bytes = Vec::new();
+ match std::io::stdin().read_to_end(&mut bytes) {
+ Ok(_) => {
+ if bytes.is_empty() {
+ return None;
+ }
+ // Handle encoding differences, ensure output is always UTF-8.
+ // First try strict UTF-8 parsing; fall back to lossy conversion
+ Some(String::from_utf8_lossy(&bytes).into_owned())
+ }
+ Err(_) => None,
+ }
+}