blendtutor_core/runner/
r.rs

1//! R execution mechanics: the R-specific half of a language run — which program
2//! and which flags — over the shared subprocess core in [`super::subprocess`].
3//!
4//! This module owns *only* the R specifics (ADR-0005, §4.1); callers depend
5//! on the [`Runner`](super::Runner) trait, not on [`RRunner`], and the
6//! spawn/timeout/temp-cwd dance is shared with every other language (§4.2).
7//!
8//! R does not declare packages (ADR-0011): R lessons use system-installed
9//! libraries, and the runner stays `Rscript --vanilla -e`. The `Interpreter`
10//! type changed from `&'static [&'static str]` to `Vec<String>` (so Python can
11//! inject `--with` flags), so R builds its fixed args dynamically — but the
12//! invocation is byte-identical.
13
14use std::time::Duration;
15
16use super::subprocess::{self, Interpreter};
17use super::{ExecutionResult, Runner, RunnerError, Timeout};
18
19/// Build the R interpreter descriptor: `Rscript --vanilla -e`. The args are
20/// fixed (R has no packages concern, ADR-0011) but built as a `Vec<String>`
21/// because `Interpreter.code_args` is now `Vec<String>` (relaxed from
22/// `&'static [&'static str]` so Python can inject runtime `--with` flags).
23fn r_interpreter() -> Interpreter {
24    Interpreter {
25        program: "Rscript",
26        code_args: vec!["--vanilla".into(), "-e".into()],
27    }
28}
29
30/// A [`Runner`] backed by a real `Rscript` subprocess.
31#[derive(Debug, Clone)]
32pub struct RRunner {
33    timeout: Timeout,
34}
35
36impl RRunner {
37    /// Build a runner that kills any execution exceeding `timeout`.
38    pub fn new(timeout: Timeout) -> Self {
39        Self { timeout }
40    }
41}
42
43impl Default for RRunner {
44    /// A 30-second bound — generous for a lesson exercise, finite for a runaway.
45    fn default() -> Self {
46        Self::new(Timeout(Duration::from_secs(30)))
47    }
48}
49
50impl Runner for RRunner {
51    async fn execute(
52        &self,
53        code: &str,
54        _checks: &[String],
55    ) -> Result<ExecutionResult, RunnerError> {
56        let interpreter = r_interpreter();
57        subprocess::run(&interpreter, code, self.timeout).await
58    }
59}