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}