diff --git a/television/channels/prototypes.rs b/television/channels/prototypes.rs index 27bce75..43da9e3 100644 --- a/television/channels/prototypes.rs +++ b/television/channels/prototypes.rs @@ -159,12 +159,42 @@ impl CommandSpec { } } +/// Execution mode for external actions +#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum ExecutionMode { + /// Fork the command as a child process (current behavior, tv stays open) + #[default] + Fork, + /// Replace the current process with the command (tv exits, command takes over) + Become, +} + +/// Output handling mode for external actions +#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum OutputMode { + /// Inherit stdin/stdout/stderr (current behavior) + #[default] + Inherit, + /// Capture output for processing + Capture, + /// Discard output silently + Discard, +} + #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq)] pub struct ActionSpec { #[serde(default)] pub description: Option, #[serde(flatten)] pub command: CommandSpec, + /// How to execute the command (fork vs become) + #[serde(default)] + pub become: bool, + /// How to handle command output + #[serde(default)] + pub output_mode: OutputMode, } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] diff --git a/television/main.rs b/television/main.rs index 7cd0884..f545427 100644 --- a/television/main.rs +++ b/television/main.rs @@ -21,7 +21,7 @@ use television::{ gh::update_local_channels, television::Mode, utils::clipboard::CLIPBOARD, - utils::command::shell_command, + utils::command::{execute_action, shell_command}, utils::{ shell::{ Shell, completion_script, render_autocomplete_script_template, @@ -123,18 +123,8 @@ async fn main() -> Result<()> { debug!("External command: {}", formatted_command); - // Execute the command with inherited stdio - let mut child = shell_command( - &formatted_command, - action_spec.command.interactive, - &action_spec.command.env, - ) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .spawn()?; - - let status = child.wait()?; + // Execute the command using the new action execution abstraction + let status = execute_action(&action_spec, &formatted_command)?; if !status.success() { eprintln!( diff --git a/television/utils/command.rs b/television/utils/command.rs index 10922a7..0ffec0b 100644 --- a/television/utils/command.rs +++ b/television/utils/command.rs @@ -4,6 +4,7 @@ use std::{collections::HashMap, process::Command}; use tracing::warn; use super::shell::Shell; +use crate::channels::prototypes::{ActionSpec, OutputMode}; pub fn shell_command( command: &str, @@ -32,3 +33,55 @@ pub fn shell_command( cmd.envs(envs).arg(command); cmd } + +/// Execute an external action with the appropriate execution mode and output handling +/// +/// Currently implements the existing behavior but designed to be extended with: +/// - `become` flag for execve behavior +/// - `output_mode` for different output handling modes +pub fn execute_action( + action_spec: &ActionSpec, + formatted_command: &str, +) -> std::io::Result { + // For now, preserve existing behavior regardless of the new flags + // In the future, this will branch based on action_spec.become and action_spec.output_mode + + let mut cmd = shell_command( + formatted_command, + action_spec.command.interactive, + &action_spec.command.env, + ); + + // Configure stdio based on output mode (future extension point) + match action_spec.output_mode { + OutputMode::Inherit => { + cmd.stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + } + OutputMode::Capture => { + // Future: capture output for processing + cmd.stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + } + OutputMode::Discard => { + // Future: discard output silently + cmd.stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + } + } + + // Execute based on become flag (future extension point) + if action_spec.become { + // Future: use execve to replace current process + // For now, use normal execution + let mut child = cmd.spawn()?; + child.wait() + } else { + // Normal fork execution (current behavior) + let mut child = cmd.spawn()?; + child.wait() + } +}