1use std::path::{Path, PathBuf};
10
11use crate::command_options::CommandOptions;
12use crate::errors::BntoError;
13
14pub trait ProcessContext: Send + Sync {
16 fn run_command(&self, cmd: &str, args: &[&str]) -> Result<Vec<u8>, BntoError>;
18
19 fn run_command_streaming(
25 &self,
26 cmd: &str,
27 args: &[&str],
28 on_output: &dyn Fn(&str),
29 ) -> Result<Vec<u8>, BntoError> {
30 let _ = on_output;
31 self.run_command(cmd, args)
32 }
33
34 fn run_command_with(
39 &self,
40 cmd: &str,
41 args: &[&str],
42 options: &CommandOptions,
43 on_output: &dyn Fn(&str),
44 ) -> Result<Vec<u8>, BntoError> {
45 let _ = options;
46 self.run_command_streaming(cmd, args, on_output)
47 }
48
49 fn temp_file(&self, suffix: &str) -> Result<PathBuf, BntoError>;
52
53 fn env_var(&self, key: &str) -> Option<String>;
55
56 fn work_dir(&self) -> Result<&Path, BntoError>;
58
59 fn find_binary(&self, name: &str) -> Option<PathBuf> {
63 let _ = name;
64 None
65 }
66
67 fn home_dir(&self) -> Option<&Path> {
70 None
71 }
72
73 fn output_dir(&self) -> Option<PathBuf> {
76 None
77 }
78
79 fn dry_run(&self) -> bool {
82 false
83 }
84}
85
86pub struct NoopContext;
89
90impl ProcessContext for NoopContext {
91 fn run_command(&self, _cmd: &str, _args: &[&str]) -> Result<Vec<u8>, BntoError> {
92 Err(BntoError::ProcessingFailed(
93 "System commands not available in browser".to_string(),
94 ))
95 }
96
97 fn temp_file(&self, _suffix: &str) -> Result<PathBuf, BntoError> {
98 Err(BntoError::ProcessingFailed(
99 "Temp files not available in browser".to_string(),
100 ))
101 }
102
103 fn env_var(&self, _key: &str) -> Option<String> {
104 None
105 }
106
107 fn work_dir(&self) -> Result<&Path, BntoError> {
108 Err(BntoError::ProcessingFailed(
109 "Working directory not available in browser".to_string(),
110 ))
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_noop_context_run_command_returns_err() {
120 let ctx = NoopContext;
121 let result = ctx.run_command("ls", &[]);
122 assert!(result.is_err());
123 assert!(
124 result
125 .unwrap_err()
126 .to_string()
127 .contains("not available in browser")
128 );
129 }
130
131 #[test]
132 fn test_noop_context_temp_file_returns_err() {
133 let ctx = NoopContext;
134 let result = ctx.temp_file(".txt");
135 assert!(result.is_err());
136 assert!(
137 result
138 .unwrap_err()
139 .to_string()
140 .contains("not available in browser")
141 );
142 }
143
144 #[test]
145 fn test_noop_context_env_var_returns_none() {
146 let ctx = NoopContext;
147 assert_eq!(ctx.env_var("PATH"), None);
148 }
149
150 #[test]
151 fn test_noop_context_streaming_falls_back_to_run_command() {
152 let ctx = NoopContext;
153 let called = std::cell::Cell::new(false);
154 let result = ctx.run_command_streaming("ls", &[], &|_| called.set(true));
155 assert!(result.is_err());
156 assert!(
157 !called.get(),
158 "on_output should not be called in noop fallback"
159 );
160 }
161
162 #[test]
163 fn test_noop_context_work_dir_returns_err() {
164 let ctx = NoopContext;
165 let result = ctx.work_dir();
166 assert!(result.is_err());
167 assert!(
168 result
169 .unwrap_err()
170 .to_string()
171 .contains("not available in browser")
172 );
173 }
174
175 #[test]
176 fn test_noop_context_find_binary_returns_none() {
177 assert!(NoopContext.find_binary("git").is_none());
178 }
179
180 #[test]
181 fn test_noop_context_home_dir_returns_none() {
182 let ctx = NoopContext;
183 assert!(ctx.home_dir().is_none());
184 }
185
186 #[test]
187 fn test_noop_context_dry_run_is_false() {
188 assert!(!NoopContext.dry_run());
189 }
190
191 #[test]
192 fn test_noop_context_output_dir_returns_none() {
193 let ctx = NoopContext;
194 assert!(ctx.output_dir().is_none());
195 }
196}