toon/shared/
string_utils.rs1use crate::shared::constants::{BACKSLASH, CARRIAGE_RETURN, DOUBLE_QUOTE, NEWLINE, TAB};
2
3#[must_use]
4pub fn escape_string(value: &str) -> String {
5 let mut out = String::with_capacity(value.len());
6 for ch in value.chars() {
7 match ch {
8 '\\' => {
9 out.push(BACKSLASH);
10 out.push(BACKSLASH);
11 }
12 '"' => {
13 out.push(BACKSLASH);
14 out.push(DOUBLE_QUOTE);
15 }
16 '\n' => {
17 out.push(BACKSLASH);
18 out.push('n');
19 }
20 '\r' => {
21 out.push(BACKSLASH);
22 out.push('r');
23 }
24 '\t' => {
25 out.push(BACKSLASH);
26 out.push('t');
27 }
28 _ => out.push(ch),
29 }
30 }
31 out
32}
33
34pub fn unescape_string(value: &str) -> Result<String, String> {
41 let mut out = String::with_capacity(value.len());
42 let mut chars = value.chars();
43
44 while let Some(ch) = chars.next() {
45 if ch == BACKSLASH {
46 let next = chars
47 .next()
48 .ok_or_else(|| "Invalid escape sequence: backslash at end of string".to_string())?;
49 match next {
50 'n' => out.push(NEWLINE),
51 't' => out.push(TAB),
52 'r' => out.push(CARRIAGE_RETURN),
53 '\\' => out.push(BACKSLASH),
54 '"' => out.push(DOUBLE_QUOTE),
55 other => {
56 return Err(format!("Invalid escape sequence: \\{other}"));
57 }
58 }
59 } else {
60 out.push(ch);
61 }
62 }
63
64 Ok(out)
65}
66
67#[must_use]
68pub fn find_closing_quote(content: &str, start: usize) -> Option<usize> {
69 let bytes = content.as_bytes();
70 let mut i = start + 1;
71 while i < bytes.len() {
72 if bytes[i] == BACKSLASH as u8 && i + 1 < bytes.len() {
73 i += 2;
74 continue;
75 }
76 if bytes[i] == DOUBLE_QUOTE as u8 {
77 return Some(i);
78 }
79 i += 1;
80 }
81 None
82}
83
84#[must_use]
85pub fn find_unquoted_char(content: &str, target: char, start: usize) -> Option<usize> {
86 let bytes = content.as_bytes();
87 let mut i = start;
88 let mut in_quotes = false;
89 while i < bytes.len() {
90 let ch = bytes[i] as char;
91 if in_quotes && ch == BACKSLASH && i + 1 < bytes.len() {
92 i += 2;
93 continue;
94 }
95 if ch == DOUBLE_QUOTE {
96 in_quotes = !in_quotes;
97 i += 1;
98 continue;
99 }
100 if ch == target && !in_quotes {
101 return Some(i);
102 }
103 i += 1;
104 }
105 None
106}