sqz_engine/
exit_marker.rs1pub fn posix_rewrite(command: &str, escaped_label: &str) -> String {
26 format!(
27 "{{ ( {command}\n); printf '\\n__SQZ_EXIT_%d__\\n' \"$?\"; }} 2>&1 | sqz compress --cmd {escaped_label}"
28 )
29}
30
31pub fn posix_rewrite_list(body: &str, escaped_label: &str) -> String {
34 posix_rewrite(body, escaped_label)
35}
36
37pub fn posix_flat_rewrite(command: &str, cmds: &[crate::shell_scan::FlatCmd]) -> String {
42 let command = command.trim_end();
43 let mut out = String::from("set -o pipefail; ");
44 let mut at = 0;
45 for cmd in cmds {
46 out.push_str(&command[at..cmd.end]);
47 if let Some(label) = &cmd.label {
48 if cmd.merge_stderr {
49 out.push_str(" 2>&1");
50 }
51 out.push_str(" | sqz compress --cmd ");
52 if label.contains(' ') {
53 out.push('\'');
54 out.push_str(label);
55 out.push('\'');
56 } else {
57 out.push_str(label);
58 }
59 }
60 at = cmd.end;
61 }
62 out.push_str(&command[at..]);
63 out
64}
65
66pub fn powershell_rewrite(command: &str, escaped_label: &str) -> String {
70 format!(
71 "& {{ {command} 2>&1; \"__SQZ_EXIT_$(if ($null -ne $LASTEXITCODE) {{ $LASTEXITCODE }} elseif ($?) {{ 0 }} else {{ 1 }})__\" }} | sqz compress --cmd {escaped_label}"
72 )
73}
74
75pub fn strip_exit_marker(input: &str) -> (String, Option<i32>) {
87 let (head, code) = strip_exit_marker_bytes(input.as_bytes());
88 (input[..head.len()].to_string(), code)
90}
91
92pub fn strip_exit_marker_bytes(input: &[u8]) -> (&[u8], Option<i32>) {
94 const PREFIX: &[u8] = b"__SQZ_EXIT_";
95 const SUFFIX: &[u8] = b"__";
96
97 let mut end = input.len();
100 while end > 0 && matches!(input[end - 1], b'\n' | b'\r') {
101 end -= 1;
102 }
103 let trimmed = &input[..end];
104 let line_start = trimmed.iter().rposition(|&b| b == b'\n').map_or(0, |i| i + 1);
105 let last_line = &trimmed[line_start..];
106
107 let Some(digits) = last_line.strip_prefix(PREFIX).and_then(|r| r.strip_suffix(SUFFIX)) else {
108 return (input, None);
109 };
110 let unsigned = digits.strip_prefix(b"-").unwrap_or(digits);
111 if unsigned.is_empty() || unsigned.len() > 10 || !unsigned.iter().all(u8::is_ascii_digit) {
112 return (input, None);
113 }
114 let Some(code) = std::str::from_utf8(digits).ok().and_then(|d| d.parse::<i64>().ok()) else {
115 return (input, None);
116 };
117
118 let mut head = &trimmed[..line_start];
121 head = head
122 .strip_suffix(b"\r\n")
123 .or_else(|| head.strip_suffix(b"\n"))
124 .unwrap_or(head);
125 (head, Some(exit_status(code)))
126}
127
128fn exit_status(code: i64) -> i32 {
130 let kept = if cfg!(windows) { code as i32 } else { (code & 0xFF) as i32 };
131 if kept == 0 && code != 0 {
132 1
133 } else {
134 kept
135 }
136}
137
138#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn strips_marker_and_restores_exact_bytes() {
146 let (out, code) = strip_exit_marker("hello\nworld\n\n__SQZ_EXIT_2__\n");
147 assert_eq!(out, "hello\nworld\n");
148 assert_eq!(code, Some(2));
149 }
150
151 #[test]
152 fn output_without_trailing_newline() {
153 let (out, code) = strip_exit_marker("no newline\n__SQZ_EXIT_42__\n");
154 assert_eq!(out, "no newline");
155 assert_eq!(code, Some(42));
156 }
157
158 #[test]
159 fn empty_output_just_marker() {
160 let (out, code) = strip_exit_marker("\n__SQZ_EXIT_0__\n");
161 assert_eq!(out, "");
162 assert_eq!(code, Some(0));
163 }
164
165 #[test]
166 fn crlf_from_powershell() {
167 let (out, code) = strip_exit_marker("line one\r\n__SQZ_EXIT_1__\r\n");
168 assert_eq!(out, "line one");
169 assert_eq!(code, Some(1));
170 }
171
172 #[test]
173 fn no_marker_passes_through() {
174 let input = "plain output\nwith lines\n";
175 let (out, code) = strip_exit_marker(input);
176 assert_eq!(out, input);
177 assert_eq!(code, None);
178 }
179
180 #[test]
181 fn marker_mid_stream_is_not_a_trailer() {
182 let input = "__SQZ_EXIT_7__\nreal output after\n";
183 let (out, code) = strip_exit_marker(input);
184 assert_eq!(out, input);
185 assert_eq!(code, None);
186 }
187
188 #[test]
189 fn malformed_markers_left_alone() {
190 for input in [
191 "x\n__SQZ_EXIT___\n",
192 "x\n__SQZ_EXIT_12a__\n",
193 "x\n__SQZ_EXIT_12345678901__\n",
194 "x\n__SQZ_EXIT_-__\n",
195 "x\n__SQZ_EXIT_--5__\n",
196 "x\n__SQZ_EXIT_+5__\n",
197 "x\n__SQZ_EXIT_5_\n",
198 "x\nSQZ_EXIT_5__\n",
199 ] {
200 let (out, code) = strip_exit_marker(input);
201 assert_eq!(out, input, "must not strip: {input:?}");
202 assert_eq!(code, None);
203 }
204 }
205
206 #[test]
207 fn negative_and_wide_codes_still_fail() {
208 let (out, code) = strip_exit_marker("fatal: crashed\n\n__SQZ_EXIT_-1073741819__\n");
209 assert_eq!(out, "fatal: crashed\n");
210 assert_eq!(code, Some(if cfg!(windows) { -1073741819 } else { 5 }));
211 let (_, code) = strip_exit_marker("x\n__SQZ_EXIT_3221225477__\n");
212 assert_eq!(code, Some(if cfg!(windows) { -1073741819 } else { 5 }));
213 let (_, code) = strip_exit_marker("x\n__SQZ_EXIT_256__\n");
214 assert_eq!(code, Some(if cfg!(windows) { 256 } else { 1 }));
215 assert_eq!(strip_exit_marker("x\n__SQZ_EXIT_4294967296__\n").1, Some(1));
216 assert_eq!(strip_exit_marker("x\n__SQZ_EXIT_-0__\n").1, Some(0));
217 let (_, code) = strip_exit_marker_bytes(b"\xff\n__SQZ_EXIT_-1__\n");
218 assert_eq!(code, Some(if cfg!(windows) { -1 } else { 255 }));
219 }
220
221 #[test]
222 fn posix_rewrite_shape() {
223 let r = posix_rewrite("git status", "'git status'");
224 assert_eq!(
225 r,
226 "{ ( git status\n); printf '\\n__SQZ_EXIT_%d__\\n' \"$?\"; } 2>&1 | sqz compress --cmd 'git status'"
227 );
228 }
229
230 #[test]
231 fn strips_marker_from_binary_output() {
232 let input = b"\xff\xfe\x00raw\n\n__SQZ_EXIT_3__\n";
233 assert_eq!(strip_exit_marker_bytes(input), (&b"\xff\xfe\x00raw\n"[..], Some(3)));
234 let plain = b"\xff no marker";
235 assert_eq!(strip_exit_marker_bytes(plain), (&plain[..], None));
236 }
237
238 #[test]
239 fn list_rewrite_shape() {
240 assert_eq!(
241 posix_rewrite_list("ls # all", "'sh: ls # all'"),
242 "{ ( ls # all\n); printf '\\n__SQZ_EXIT_%d__\\n' \"$?\"; } 2>&1 | sqz compress --cmd 'sh: ls # all'"
243 );
244 }
245
246 #[test]
247 fn flat_rewrite_shape() {
248 let cmd = "cd sub && cargo test 2>&1; git log -3 # recent\n";
249 let parts = crate::shell_scan::flat(cmd).unwrap();
250 assert_eq!(
251 posix_flat_rewrite(cmd, &parts),
252 "set -o pipefail; cd sub && cargo test 2>&1 | sqz compress --cmd 'cargo test'; \
253 git log -3 2>&1 | sqz compress --cmd 'git log -3' # recent"
254 );
255 let parts = crate::shell_scan::flat("ls").unwrap();
256 assert_eq!(posix_flat_rewrite("ls", &parts), "set -o pipefail; ls 2>&1 | sqz compress --cmd ls");
257 }
258
259 #[test]
260 fn round_trip_posix_semantics() {
261 let produced = format!("ls: cannot access '/nope': No such file or directory\n\n__SQZ_EXIT_{}__\n", 2);
263 let (out, code) = strip_exit_marker(&produced);
264 assert_eq!(out, "ls: cannot access '/nope': No such file or directory\n");
265 assert_eq!(code, Some(2));
266 }
267}