From 31045c8971ddc3e796ffc0a4bb21df38ef78dc88 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 07:53:17 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B9=20Extract=20process=20execution=20?= =?UTF-8?q?closures=20in=20stdlib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extracted inline closures for `run`, `spawn`, `exit`, and `pid` in `register_process` into separate top-level functions `process_run`, `process_spawn`, `process_exit`, and `process_pid`. - Improves readability, maintainability, and clarifies the API surface. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- stdlib/src/process.rs | 301 ++++++++++++++++++++++-------------------- 1 file changed, 159 insertions(+), 142 deletions(-) diff --git a/stdlib/src/process.rs b/stdlib/src/process.rs index b22f3ee4..f050f5e8 100644 --- a/stdlib/src/process.rs +++ b/stdlib/src/process.rs @@ -5,11 +5,165 @@ use std::collections::HashMap; use std::process::Command; use std::rc::Rc; use techscript_runtime::{ - context::Capability, + context::{Capability, RuntimeContext}, error::{RuntimeError, RuntimeErrorKind}, value::RuntimeValue, }; +fn process_run( + ctx: &mut RuntimeContext, + args: Vec, +) -> Result { + if !ctx.config.capabilities.contains(&Capability::Process) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: Process capability is denied".to_string(), + ), + None, + None, + )); + } + let cmd = args[0].try_into_string()?; + if cmd.contains("..") || cmd.contains('\0') { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: Path traversal or null bytes in command are denied" + .to_string(), + ), + None, + None, + )); + } + let args_list = match &args[1] { + RuntimeValue::List { items, .. } => { + let mut list = Vec::new(); + for item in items.borrow().iter() { + list.push(item.try_into_string()?); + } + list + } + other => { + return Err(RuntimeError::new( + RuntimeErrorKind::TypeMismatch { + expected: "List".to_string(), + found: other.runtime_type().to_string(), + }, + None, + None, + )) + } + }; + + let output = Command::new(cmd).args(args_list).output().map_err(|e| { + RuntimeError::new( + RuntimeErrorKind::InvalidOperation(format!("Failed to execute command: {}", e)), + None, + None, + ) + })?; + + let mut res_map = IndexMap::new(); + res_map.insert( + "stdout".to_string(), + RuntimeValue::Str(String::from_utf8_lossy(&output.stdout).to_string()), + ); + res_map.insert( + "stderr".to_string(), + RuntimeValue::Str(String::from_utf8_lossy(&output.stderr).to_string()), + ); + res_map.insert( + "code".to_string(), + RuntimeValue::Int(output.status.code().unwrap_or(-1) as i64), + ); + + Ok(RuntimeValue::Map { + entries: Rc::new(RefCell::new(res_map)), + is_const: false, + }) +} + +fn process_spawn( + ctx: &mut RuntimeContext, + args: Vec, +) -> Result { + if !ctx.config.capabilities.contains(&Capability::Process) { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: Process capability is denied".to_string(), + ), + None, + None, + )); + } + let cmd = match &args[0] { + RuntimeValue::Str(s) => s.clone(), + _ => { + return Err(RuntimeError::new( + RuntimeErrorKind::TypeMismatch { + expected: "string".to_string(), + found: "other".to_string(), + }, + None, + None, + )) + } + }; + + if cmd.contains("..") || cmd.contains('\0') { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation( + "Security policy violation: Path traversal or null bytes in command are denied" + .to_string(), + ), + None, + None, + )); + } + + let parsed = shlex::split(&cmd).ok_or_else(|| { + RuntimeError::new( + RuntimeErrorKind::InvalidOperation("Failed to parse command string".to_string()), + None, + None, + ) + })?; + + if parsed.is_empty() { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation("Empty command string".to_string()), + None, + None, + )); + } + + Command::new(&parsed[0]) + .args(&parsed[1..]) + .spawn() + .map_err(|e| { + RuntimeError::new( + RuntimeErrorKind::InvalidOperation(e.to_string()), + None, + None, + ) + })?; + Ok(RuntimeValue::Null) +} + +fn process_exit( + _ctx: &mut RuntimeContext, + args: Vec, +) -> Result { + let code = args[0].try_into_int()? as i32; + std::process::exit(code); +} + +fn process_pid( + _ctx: &mut RuntimeContext, + _args: Vec, +) -> Result { + Ok(RuntimeValue::Int(std::process::id() as i64)) +} + impl StdlibRegistry { pub fn register_process(&mut self) { let mut exports: HashMap> = @@ -20,77 +174,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "run".to_string(), arity: 2, - callback: |ctx, args| { - if !ctx.config.capabilities.contains(&Capability::Process) { - return Err(RuntimeError::new( - RuntimeErrorKind::InvalidOperation( - "Security policy violation: Process capability is denied" - .to_string(), - ), - None, - None, - )); - } - let cmd = args[0].try_into_string()?; - if cmd.contains("..") || cmd.contains('\0') { - return Err(RuntimeError::new( - RuntimeErrorKind::InvalidOperation( - "Security policy violation: Path traversal or null bytes in command are denied".to_string(), - ), - None, - None, - )); - } - let args_list = match &args[1] { - RuntimeValue::List { items, .. } => { - let mut list = Vec::new(); - for item in items.borrow().iter() { - list.push(item.try_into_string()?); - } - list - } - other => { - return Err(RuntimeError::new( - RuntimeErrorKind::TypeMismatch { - expected: "List".to_string(), - found: other.runtime_type().to_string(), - }, - None, - None, - )) - } - }; - - let output = Command::new(cmd).args(args_list).output().map_err(|e| { - RuntimeError::new( - RuntimeErrorKind::InvalidOperation(format!( - "Failed to execute command: {}", - e - )), - None, - None, - ) - })?; - - let mut res_map = IndexMap::new(); - res_map.insert( - "stdout".to_string(), - RuntimeValue::Str(String::from_utf8_lossy(&output.stdout).to_string()), - ); - res_map.insert( - "stderr".to_string(), - RuntimeValue::Str(String::from_utf8_lossy(&output.stderr).to_string()), - ); - res_map.insert( - "code".to_string(), - RuntimeValue::Int(output.status.code().unwrap_or(-1) as i64), - ); - - Ok(RuntimeValue::Map { - entries: Rc::new(RefCell::new(res_map)), - is_const: false, - }) - }, + callback: process_run, }), ); @@ -99,71 +183,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "spawn".to_string(), arity: 1, - callback: |ctx, args| { - if !ctx.config.capabilities.contains(&Capability::Process) { - return Err(RuntimeError::new( - RuntimeErrorKind::InvalidOperation( - "Security policy violation: Process capability is denied" - .to_string(), - ), - None, - None, - )); - } - let cmd = match &args[0] { - RuntimeValue::Str(s) => s.clone(), - _ => { - return Err(RuntimeError::new( - RuntimeErrorKind::TypeMismatch { - expected: "string".to_string(), - found: "other".to_string(), - }, - None, - None, - )) - } - }; - - if cmd.contains("..") || cmd.contains('\0') { - return Err(RuntimeError::new( - RuntimeErrorKind::InvalidOperation( - "Security policy violation: Path traversal or null bytes in command are denied".to_string(), - ), - None, - None, - )); - } - - let parsed = shlex::split(&cmd).ok_or_else(|| { - RuntimeError::new( - RuntimeErrorKind::InvalidOperation( - "Failed to parse command string".to_string(), - ), - None, - None, - ) - })?; - - if parsed.is_empty() { - return Err(RuntimeError::new( - RuntimeErrorKind::InvalidOperation("Empty command string".to_string()), - None, - None, - )); - } - - Command::new(&parsed[0]) - .args(&parsed[1..]) - .spawn() - .map_err(|e| { - RuntimeError::new( - RuntimeErrorKind::InvalidOperation(e.to_string()), - None, - None, - ) - })?; - Ok(RuntimeValue::Null) - }, + callback: process_spawn, }), ); @@ -172,10 +192,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "exit".to_string(), arity: 1, - callback: |_ctx, args| { - let code = args[0].try_into_int()? as i32; - std::process::exit(code); - }, + callback: process_exit, }), ); @@ -184,7 +201,7 @@ impl StdlibRegistry { Rc::new(StdFunction { name: "pid".to_string(), arity: 0, - callback: |_ctx, _args| Ok(RuntimeValue::Int(std::process::id() as i64)), + callback: process_pid, }), );