Skip to main content

bnto_core/
context.rs

1// Controlled system access for node processors.
2//
3// Processors that wrap external tools (yt-dlp, ffmpeg) need to run commands,
4// create temp files, and read env vars. This trait is the boundary:
5//   - Browser (WASM): `NoopContext` — all methods return errors
6//   - CLI (native): `NativeContext` — full `std::process` access
7//   - Desktop (future): `SandboxedContext` — scoped to approved directories
8
9use std::path::{Path, PathBuf};
10
11use crate::errors::BntoError;
12
13/// System access boundary for processors that need external tools.
14pub trait ProcessContext: Send + Sync {
15    /// Run an external command, capturing stdout.
16    fn run_command(&self, cmd: &str, args: &[&str]) -> Result<Vec<u8>, BntoError>;
17
18    /// Create a temporary file, returning its path.
19    fn temp_file(&self, suffix: &str) -> Result<PathBuf, BntoError>;
20
21    /// Read an environment variable.
22    fn env_var(&self, key: &str) -> Option<String>;
23
24    /// Get the working directory for this execution.
25    fn work_dir(&self) -> Result<&Path, BntoError>;
26}
27
28/// No-op context for browser (WASM) execution.
29/// All system-access methods return errors — browser has no shell.
30pub struct NoopContext;
31
32impl ProcessContext for NoopContext {
33    fn run_command(&self, _cmd: &str, _args: &[&str]) -> Result<Vec<u8>, BntoError> {
34        Err(BntoError::ProcessingFailed(
35            "System commands not available in browser".to_string(),
36        ))
37    }
38
39    fn temp_file(&self, _suffix: &str) -> Result<PathBuf, BntoError> {
40        Err(BntoError::ProcessingFailed(
41            "Temp files not available in browser".to_string(),
42        ))
43    }
44
45    fn env_var(&self, _key: &str) -> Option<String> {
46        None
47    }
48
49    fn work_dir(&self) -> Result<&Path, BntoError> {
50        Err(BntoError::ProcessingFailed(
51            "Working directory not available in browser".to_string(),
52        ))
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_noop_context_run_command_returns_err() {
62        let ctx = NoopContext;
63        let result = ctx.run_command("ls", &[]);
64        assert!(result.is_err());
65        assert!(
66            result
67                .unwrap_err()
68                .to_string()
69                .contains("not available in browser")
70        );
71    }
72
73    #[test]
74    fn test_noop_context_temp_file_returns_err() {
75        let ctx = NoopContext;
76        let result = ctx.temp_file(".txt");
77        assert!(result.is_err());
78        assert!(
79            result
80                .unwrap_err()
81                .to_string()
82                .contains("not available in browser")
83        );
84    }
85
86    #[test]
87    fn test_noop_context_env_var_returns_none() {
88        let ctx = NoopContext;
89        assert_eq!(ctx.env_var("PATH"), None);
90    }
91
92    #[test]
93    fn test_noop_context_work_dir_returns_err() {
94        let ctx = NoopContext;
95        let result = ctx.work_dir();
96        assert!(result.is_err());
97        assert!(
98            result
99                .unwrap_err()
100                .to_string()
101                .contains("not available in browser")
102        );
103    }
104}