Skip to main content

perl_subprocess_runtime/os_runtime/
mod.rs

1//! OS-backed subprocess runtime.
2
3mod invocation;
4mod path_selection;
5mod process;
6mod validation;
7#[cfg(windows)]
8mod windows;
9
10pub(crate) use invocation::resolve_command_invocation;
11use process::run_os_command;
12
13use crate::{SubprocessError, SubprocessOutput, SubprocessRuntime};
14
15// `select_path_candidate` and `candidate_priority` are cross-platform so they
16// are exported for test use on all platforms — not just Windows.  This lets
17// the ripr quality gate observe call paths on Linux CI runners.
18#[cfg(test)]
19pub(crate) use path_selection::{candidate_priority, select_path_candidate};
20
21// `windows_program_priority` is the historical name for `candidate_priority`
22// used in Windows-specific tests.  Export it as an alias on Windows so tests
23// that use the old name continue to compile.
24#[cfg(all(windows, test))]
25pub(crate) use path_selection::candidate_priority as windows_program_priority;
26
27#[cfg(all(windows, test))]
28pub(crate) use windows::{resolve_cmd_exe, windows_quote_for_cmd};
29
30/// Re-export of [`windows::resolve_windows_program`] for use by
31/// [`crate::resolve_program`].  The inner function is `pub(crate)`; this
32/// wrapper lifts it to `pub(super)` so `lib.rs` can call it without making
33/// the Windows-specific internals part of the public API.
34#[cfg(windows)]
35pub(super) fn resolve_windows_program_pub(program: &str) -> Option<String> {
36    windows::resolve_windows_program(program)
37}
38
39/// Default implementation using `std::process::Command`.
40pub struct OsSubprocessRuntime {
41    timeout_secs: Option<u64>,
42}
43
44impl OsSubprocessRuntime {
45    /// Create a new OS subprocess runtime with no timeout.
46    pub fn new() -> Self {
47        Self { timeout_secs: None }
48    }
49
50    /// Create a new OS subprocess runtime with the given wall-clock timeout.
51    ///
52    /// If the subprocess does not complete within `timeout_secs` seconds the
53    /// call returns a `SubprocessError` with a "timed out" message and attempts
54    /// to terminate the spawned process before returning.
55    ///
56    /// # Stdin size caveat
57    ///
58    /// Stdin data is written synchronously before the timeout poll loop begins.
59    /// If the subprocess hangs before consuming stdin and the data exceeds the
60    /// OS pipe buffer (~64 KiB on Linux), `run_command` will block in the write
61    /// phase and the timeout will not fire. For typical Perl source files this
62    /// is not a concern.
63    ///
64    /// # Panics
65    ///
66    /// Panics if `timeout_secs` is zero (a zero-second timeout would time out
67    /// every command immediately and is almost certainly a caller bug).
68    pub fn with_timeout(timeout_secs: u64) -> Self {
69        assert!(timeout_secs > 0, "timeout_secs must be greater than zero");
70        Self { timeout_secs: Some(timeout_secs) }
71    }
72}
73
74impl Default for OsSubprocessRuntime {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl SubprocessRuntime for OsSubprocessRuntime {
81    fn run_command(
82        &self,
83        program: &str,
84        args: &[&str],
85        stdin: Option<&[u8]>,
86    ) -> Result<SubprocessOutput, SubprocessError> {
87        run_os_command(program, args, stdin, self.timeout_secs)
88    }
89}