agentmux_srv\backend/
trimquotes.rs1#![allow(dead_code)]
2pub fn trim_quotes(s: &str) -> (String, bool) {
29 if s.len() <= 2 || !s.starts_with('"') {
32 return (s.to_string(), false);
33 }
34
35 match unescape_quoted(s) {
36 Some(unquoted) => (unquoted, true),
37 None => (s.to_string(), false),
38 }
39}
40
41pub fn try_trim_quotes(s: &str) -> String {
44 trim_quotes(s).0
45}
46
47pub fn replace_quotes(s: &str, should_replace: bool) -> String {
49 if should_replace {
50 escape_and_quote(s)
51 } else {
52 s.to_string()
53 }
54}
55
56fn unescape_quoted(s: &str) -> Option<String> {
59 if !s.starts_with('"') || !s.ends_with('"') || s.len() < 2 {
60 return None;
61 }
62
63 let inner = &s[1..s.len() - 1];
64 let mut result = String::with_capacity(inner.len());
65 let mut chars = inner.chars();
66
67 while let Some(c) = chars.next() {
68 if c == '\\' {
69 match chars.next() {
70 Some('\\') => result.push('\\'),
71 Some('"') => result.push('"'),
72 Some('n') => result.push('\n'),
73 Some('r') => result.push('\r'),
74 Some('t') => result.push('\t'),
75 Some('0') => result.push('\0'),
76 Some('a') => result.push('\x07'), Some('b') => result.push('\x08'), Some('f') => result.push('\x0C'), Some('v') => result.push('\x0B'), Some(other) => {
81 result.push('\\');
83 result.push(other);
84 }
85 None => return None, }
87 } else if c == '"' {
88 return None;
90 } else {
91 result.push(c);
92 }
93 }
94
95 Some(result)
96}
97
98fn escape_and_quote(s: &str) -> String {
100 let mut result = String::with_capacity(s.len() + 2);
101 result.push('"');
102 for c in s.chars() {
103 match c {
104 '"' => result.push_str("\\\""),
105 '\\' => result.push_str("\\\\"),
106 '\n' => result.push_str("\\n"),
107 '\r' => result.push_str("\\r"),
108 '\t' => result.push_str("\\t"),
109 '\0' => result.push_str("\\0"),
110 _ => result.push(c),
111 }
112 }
113 result.push('"');
114 result
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_trim_quotes_basic() {
123 let (s, ok) = trim_quotes(r#""hello""#);
124 assert_eq!(s, "hello");
125 assert!(ok);
126 }
127
128 #[test]
129 fn test_trim_quotes_with_spaces() {
130 let (s, ok) = trim_quotes(r#""hello world""#);
131 assert_eq!(s, "hello world");
132 assert!(ok);
133 }
134
135 #[test]
136 fn test_trim_quotes_with_escapes() {
137 let (s, ok) = trim_quotes(r#""hello\nworld""#);
138 assert_eq!(s, "hello\nworld");
139 assert!(ok);
140 }
141
142 #[test]
143 fn test_trim_quotes_escaped_quote() {
144 let (s, ok) = trim_quotes(r#""hello\"world""#);
145 assert_eq!(s, "hello\"world");
146 assert!(ok);
147 }
148
149 #[test]
150 fn test_trim_quotes_escaped_backslash() {
151 let (s, ok) = trim_quotes(r#""path\\to\\file""#);
152 assert_eq!(s, "path\\to\\file");
153 assert!(ok);
154 }
155
156 #[test]
157 fn test_trim_quotes_not_quoted() {
158 let (s, ok) = trim_quotes("unquoted");
159 assert_eq!(s, "unquoted");
160 assert!(!ok);
161 }
162
163 #[test]
164 fn test_trim_quotes_empty() {
165 let (s, ok) = trim_quotes("");
166 assert_eq!(s, "");
167 assert!(!ok);
168 }
169
170 #[test]
171 fn test_trim_quotes_single_char() {
172 let (s, ok) = trim_quotes("a");
173 assert_eq!(s, "a");
174 assert!(!ok);
175 }
176
177 #[test]
178 fn test_trim_quotes_empty_quoted() {
179 let (s, ok) = trim_quotes(r#""""#);
181 assert_eq!(s, r#""""#);
182 assert!(!ok);
183 }
184
185 #[test]
186 fn test_try_trim_quotes() {
187 assert_eq!(try_trim_quotes(r#""test""#), "test");
188 assert_eq!(try_trim_quotes("unquoted"), "unquoted");
189 }
190
191 #[test]
192 fn test_replace_quotes_true() {
193 let result = replace_quotes("hello", true);
194 assert_eq!(result, r#""hello""#);
195 }
196
197 #[test]
198 fn test_replace_quotes_false() {
199 let result = replace_quotes("hello", false);
200 assert_eq!(result, "hello");
201 }
202
203 #[test]
204 fn test_replace_quotes_with_special() {
205 let result = replace_quotes("hello\nworld", true);
206 assert_eq!(result, r#""hello\nworld""#);
207 }
208
209 #[test]
210 fn test_replace_quotes_with_quotes() {
211 let result = replace_quotes(r#"say "hi""#, true);
212 assert_eq!(result, r#""say \"hi\"""#);
213 }
214
215 #[test]
216 fn test_escape_and_quote_roundtrip() {
217 let original = "hello \"world\"\nnew\\line";
218 let quoted = escape_and_quote(original);
219 let (unquoted, ok) = trim_quotes("ed);
220 assert!(ok);
221 assert_eq!(unquoted, original);
222 }
223
224 #[test]
225 fn test_all_escape_sequences() {
226 let (s, ok) = trim_quotes(r#""tab\there\nnewline\rreturn\0null""#);
227 assert!(ok);
228 assert!(s.contains('\t'));
229 assert!(s.contains('\n'));
230 assert!(s.contains('\r'));
231 assert!(s.contains('\0'));
232 }
233
234 #[test]
235 fn test_trailing_backslash_fails() {
236 let (s, ok) = trim_quotes(r#""trailing\"#);
237 assert!(!ok);
238 assert_eq!(s, r#""trailing\"#);
239 }
240}