1use std::path::{Path, PathBuf};
10
11use crate::errors::BntoError;
12
13pub trait ProcessContext: Send + Sync {
15 fn run_command(&self, cmd: &str, args: &[&str]) -> Result<Vec<u8>, BntoError>;
17
18 fn run_command_streaming(
24 &self,
25 cmd: &str,
26 args: &[&str],
27 on_output: &dyn Fn(&str),
28 ) -> Result<Vec<u8>, BntoError> {
29 let _ = on_output;
30 self.run_command(cmd, args)
31 }
32
33 fn temp_file(&self, suffix: &str) -> Result<PathBuf, BntoError>;
36
37 fn env_var(&self, key: &str) -> Option<String>;
39
40 fn work_dir(&self) -> Result<&Path, BntoError>;
42
43 fn home_dir(&self) -> Option<&Path> {
46 None
47 }
48
49 fn output_dir(&self) -> Option<PathBuf> {
52 None
53 }
54}
55
56pub struct NoopContext;
59
60impl ProcessContext for NoopContext {
61 fn run_command(&self, _cmd: &str, _args: &[&str]) -> Result<Vec<u8>, BntoError> {
62 Err(BntoError::ProcessingFailed(
63 "System commands not available in browser".to_string(),
64 ))
65 }
66
67 fn temp_file(&self, _suffix: &str) -> Result<PathBuf, BntoError> {
68 Err(BntoError::ProcessingFailed(
69 "Temp files not available in browser".to_string(),
70 ))
71 }
72
73 fn env_var(&self, _key: &str) -> Option<String> {
74 None
75 }
76
77 fn work_dir(&self) -> Result<&Path, BntoError> {
78 Err(BntoError::ProcessingFailed(
79 "Working directory not available in browser".to_string(),
80 ))
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_noop_context_run_command_returns_err() {
90 let ctx = NoopContext;
91 let result = ctx.run_command("ls", &[]);
92 assert!(result.is_err());
93 assert!(
94 result
95 .unwrap_err()
96 .to_string()
97 .contains("not available in browser")
98 );
99 }
100
101 #[test]
102 fn test_noop_context_temp_file_returns_err() {
103 let ctx = NoopContext;
104 let result = ctx.temp_file(".txt");
105 assert!(result.is_err());
106 assert!(
107 result
108 .unwrap_err()
109 .to_string()
110 .contains("not available in browser")
111 );
112 }
113
114 #[test]
115 fn test_noop_context_env_var_returns_none() {
116 let ctx = NoopContext;
117 assert_eq!(ctx.env_var("PATH"), None);
118 }
119
120 #[test]
121 fn test_noop_context_streaming_falls_back_to_run_command() {
122 let ctx = NoopContext;
123 let called = std::cell::Cell::new(false);
124 let result = ctx.run_command_streaming("ls", &[], &|_| called.set(true));
125 assert!(result.is_err());
126 assert!(
127 !called.get(),
128 "on_output should not be called in noop fallback"
129 );
130 }
131
132 #[test]
133 fn test_noop_context_work_dir_returns_err() {
134 let ctx = NoopContext;
135 let result = ctx.work_dir();
136 assert!(result.is_err());
137 assert!(
138 result
139 .unwrap_err()
140 .to_string()
141 .contains("not available in browser")
142 );
143 }
144
145 #[test]
146 fn test_noop_context_home_dir_returns_none() {
147 let ctx = NoopContext;
148 assert!(ctx.home_dir().is_none());
149 }
150
151 #[test]
152 fn test_noop_context_output_dir_returns_none() {
153 let ctx = NoopContext;
154 assert!(ctx.output_dir().is_none());
155 }
156}