From 5e31a81cd2207f053b8cd8ad84ebe2a2f691b29d Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Wed, 10 Jun 2026 22:01:45 +0200 Subject: chore: Remove some unused rust code --- crates/atuin-scripts/src/execution.rs | 286 ---------------------------------- 1 file changed, 286 deletions(-) delete mode 100644 crates/atuin-scripts/src/execution.rs (limited to 'crates/atuin-scripts/src/execution.rs') diff --git a/crates/atuin-scripts/src/execution.rs b/crates/atuin-scripts/src/execution.rs deleted file mode 100644 index 5bf94aaa..00000000 --- a/crates/atuin-scripts/src/execution.rs +++ /dev/null @@ -1,286 +0,0 @@ -use crate::store::script::Script; -use eyre::Result; -use std::collections::{HashMap, HashSet}; -use std::process::Stdio; -use tempfile::NamedTempFile; -use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader}; -use tokio::sync::mpsc; -use tokio::task; -use tracing::debug; - -// Helper function to build a complete script with shebang -pub fn build_executable_script(script: String, shebang: String) -> String { - if shebang.is_empty() { - // Default to bash if no shebang is provided - format!("#!/usr/bin/env bash\n{script}") - } else if script.starts_with("#!") { - format!("{shebang}\n{script}") - } else { - format!("#!{shebang}\n{script}") - } -} - -/// Represents the communication channels for an interactive script -pub struct ScriptSession { - /// Channel to send input to the script - pub stdin_tx: mpsc::Sender, - /// Exit code of the process once it completes - pub exit_code_rx: mpsc::Receiver, -} - -impl ScriptSession { - /// Send input to the running script - pub async fn send_input(&self, input: String) -> Result<(), mpsc::error::SendError> { - self.stdin_tx.send(input).await - } - - /// Wait for the script to complete and get the exit code - pub async fn wait_for_exit(&mut self) -> Option { - self.exit_code_rx.recv().await - } -} - -fn setup_template(script: &Script) -> Result> { - let mut env = minijinja::Environment::new(); - env.set_trim_blocks(true); - env.add_template("script", script.script.as_str())?; - - Ok(env) -} - -/// Template a script with the given context -pub fn template_script( - script: &Script, - context: &HashMap, -) -> Result { - let env = setup_template(script)?; - let template = env.get_template("script")?; - let rendered = template.render(context)?; - - Ok(rendered) -} - -/// Get the variables that need to be templated in a script -pub fn template_variables(script: &Script) -> Result> { - let env = setup_template(script)?; - let template = env.get_template("script")?; - - Ok(template.undeclared_variables(true)) -} - -/// Execute a script interactively, allowing for ongoing stdin/stdout interaction -pub async fn execute_script_interactive( - script: String, - shebang: String, -) -> Result> { - // Create a temporary file for the script - let temp_file = NamedTempFile::new()?; - let temp_path = temp_file.path().to_path_buf(); - - debug!("creating temp file at {}", temp_path.display()); - - // Extract interpreter from shebang for fallback execution - let interpreter = if !shebang.is_empty() { - shebang.trim_start_matches("#!").trim().to_string() - } else { - "/usr/bin/env bash".to_string() - }; - - // Write script content to the temp file, including the shebang - let full_script_content = build_executable_script(script.clone(), shebang.clone()); - - debug!("writing script content to temp file"); - tokio::fs::write(&temp_path, &full_script_content).await?; - - // Make it executable on Unix systems - #[cfg(unix)] - { - debug!("making script executable"); - use std::os::unix::fs::PermissionsExt; - let mut perms = std::fs::metadata(&temp_path)?.permissions(); - perms.set_mode(0o755); - std::fs::set_permissions(&temp_path, perms)?; - } - - // Store the temp_file to prevent it from being dropped - // This ensures it won't be deleted while the script is running - let _keep_temp_file = temp_file; - - debug!("attempting direct script execution"); - let mut child_result = tokio::process::Command::new(temp_path.to_str().unwrap()) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn(); - - // If direct execution fails, try using the interpreter - if let Err(e) = &child_result { - debug!("direct execution failed: {}, trying with interpreter", e); - - // When falling back to interpreter, remove the shebang from the file - // Some interpreters don't handle scripts with shebangs well - debug!("writing script content without shebang for interpreter execution"); - tokio::fs::write(&temp_path, &script).await?; - - // Parse the interpreter command - let parts: Vec<&str> = interpreter.split_whitespace().collect(); - if !parts.is_empty() { - let mut cmd = tokio::process::Command::new(parts[0]); - - // Add any interpreter args - for i in parts.iter().skip(1) { - cmd.arg(i); - } - - // Add the script path - cmd.arg(temp_path.to_str().unwrap()); - - // Try with the interpreter - child_result = cmd - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn(); - } - } - - // If it still fails, return the error - let mut child = match child_result { - Ok(child) => child, - Err(e) => { - return Err(format!("Failed to execute script: {e}").into()); - } - }; - - // Get handles to stdin, stdout, stderr - let mut stdin = child - .stdin - .take() - .ok_or_else(|| "Failed to open child process stdin".to_string())?; - let stdout = child - .stdout - .take() - .ok_or_else(|| "Failed to open child process stdout".to_string())?; - let stderr = child - .stderr - .take() - .ok_or_else(|| "Failed to open child process stderr".to_string())?; - - // Create channels for the interactive session - let (stdin_tx, mut stdin_rx) = mpsc::channel::(32); - let (exit_code_tx, exit_code_rx) = mpsc::channel::(1); - - // handle user stdin - debug!("spawning stdin handler"); - tokio::spawn(async move { - while let Some(input) = stdin_rx.recv().await { - if let Err(e) = stdin.write_all(input.as_bytes()).await { - eprintln!("Error writing to stdin: {e}"); - break; - } - if let Err(e) = stdin.flush().await { - eprintln!("Error flushing stdin: {e}"); - break; - } - } - // when the channel closes (sender dropped), we let stdin close naturally - }); - - // handle stdout - debug!("spawning stdout handler"); - let stdout_handle = task::spawn(async move { - let mut stdout_reader = BufReader::new(stdout); - let mut buffer = [0u8; 1024]; - let mut stdout_writer = tokio::io::stdout(); - - loop { - match stdout_reader.read(&mut buffer).await { - Ok(0) => break, // End of stdout - Ok(n) => { - if let Err(e) = stdout_writer.write_all(&buffer[0..n]).await { - eprintln!("Error writing to stdout: {e}"); - break; - } - if let Err(e) = stdout_writer.flush().await { - eprintln!("Error flushing stdout: {e}"); - break; - } - } - Err(e) => { - eprintln!("Error reading from process stdout: {e}"); - break; - } - } - } - }); - - // Process stderr in a separate task - debug!("spawning stderr handler"); - let stderr_handle = task::spawn(async move { - let mut stderr_reader = BufReader::new(stderr); - let mut buffer = [0u8; 1024]; - let mut stderr_writer = tokio::io::stderr(); - - loop { - match stderr_reader.read(&mut buffer).await { - Ok(0) => break, // End of stderr - Ok(n) => { - if let Err(e) = stderr_writer.write_all(&buffer[0..n]).await { - eprintln!("Error writing to stderr: {e}"); - break; - } - if let Err(e) = stderr_writer.flush().await { - eprintln!("Error flushing stderr: {e}"); - break; - } - } - Err(e) => { - eprintln!("Error reading from process stderr: {e}"); - break; - } - } - } - }); - - // Spawn a task to wait for the child process to complete - debug!("spawning exit code handler"); - let _keep_temp_file_clone = _keep_temp_file; - tokio::spawn(async move { - // Keep the temp file alive until the process completes - let _temp_file_ref = _keep_temp_file_clone; - - // Wait for the child process to complete - let status = match child.wait().await { - Ok(status) => { - debug!("Process exited with status: {:?}", status); - status - } - Err(e) => { - eprintln!("Error waiting for child process: {e}"); - // Send a default error code - let _ = exit_code_tx.send(-1).await; - return; - } - }; - - // Wait for stdout/stderr tasks to complete - if let Err(e) = stdout_handle.await { - eprintln!("Error joining stdout task: {e}"); - } - - if let Err(e) = stderr_handle.await { - eprintln!("Error joining stderr task: {e}"); - } - - // Send the exit code - let exit_code = status.code().unwrap_or(-1); - debug!("Sending exit code: {}", exit_code); - let _ = exit_code_tx.send(exit_code).await; - }); - - // Return the communication channels as a ScriptSession - Ok(ScriptSession { - stdin_tx, - exit_code_rx, - }) -} -- cgit v1.3.1