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 find_binary(&self, name: &str) -> Option<PathBuf> {
47 let _ = name;
48 None
49 }
50
51 fn home_dir(&self) -> Option<&Path> {
54 None
55 }
56
57 fn output_dir(&self) -> Option<PathBuf> {
60 None
61 }
62
63 fn dry_run(&self) -> bool {
66 false
67 }
68}
69
70pub struct NoopContext;
73
74impl ProcessContext for NoopContext {
75 fn run_command(&self, _cmd: &str, _args: &[&str]) -> Result<Vec<u8>, BntoError> {
76 Err(BntoError::ProcessingFailed(
77 "System commands not available in browser".to_string(),
78 ))
79 }
80
81 fn temp_file(&self, _suffix: &str) -> Result<PathBuf, BntoError> {
82 Err(BntoError::ProcessingFailed(
83 "Temp files not available in browser".to_string(),
84 ))
85 }
86
87 fn env_var(&self, _key: &str) -> Option<String> {
88 None
89 }
90
91 fn work_dir(&self) -> Result<&Path, BntoError> {
92 Err(BntoError::ProcessingFailed(
93 "Working directory not available in browser".to_string(),
94 ))
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_noop_context_run_command_returns_err() {
104 let ctx = NoopContext;
105 let result = ctx.run_command("ls", &[]);
106 assert!(result.is_err());
107 assert!(
108 result
109 .unwrap_err()
110 .to_string()
111 .contains("not available in browser")
112 );
113 }
114
115 #[test]
116 fn test_noop_context_temp_file_returns_err() {
117 let ctx = NoopContext;
118 let result = ctx.temp_file(".txt");
119 assert!(result.is_err());
120 assert!(
121 result
122 .unwrap_err()
123 .to_string()
124 .contains("not available in browser")
125 );
126 }
127
128 #[test]
129 fn test_noop_context_env_var_returns_none() {
130 let ctx = NoopContext;
131 assert_eq!(ctx.env_var("PATH"), None);
132 }
133
134 #[test]
135 fn test_noop_context_streaming_falls_back_to_run_command() {
136 let ctx = NoopContext;
137 let called = std::cell::Cell::new(false);
138 let result = ctx.run_command_streaming("ls", &[], &|_| called.set(true));
139 assert!(result.is_err());
140 assert!(
141 !called.get(),
142 "on_output should not be called in noop fallback"
143 );
144 }
145
146 #[test]
147 fn test_noop_context_work_dir_returns_err() {
148 let ctx = NoopContext;
149 let result = ctx.work_dir();
150 assert!(result.is_err());
151 assert!(
152 result
153 .unwrap_err()
154 .to_string()
155 .contains("not available in browser")
156 );
157 }
158
159 #[test]
160 fn test_noop_context_find_binary_returns_none() {
161 assert!(NoopContext.find_binary("git").is_none());
162 }
163
164 #[test]
165 fn test_noop_context_home_dir_returns_none() {
166 let ctx = NoopContext;
167 assert!(ctx.home_dir().is_none());
168 }
169
170 #[test]
171 fn test_noop_context_dry_run_is_false() {
172 assert!(!NoopContext.dry_run());
173 }
174
175 #[test]
176 fn test_noop_context_output_dir_returns_none() {
177 let ctx = NoopContext;
178 assert!(ctx.output_dir().is_none());
179 }
180}