1#[derive(Debug)]
2pub(crate) struct UnescapeError {
3 seq: String,
4}
5
6impl UnescapeError {
7 fn new(seq: char) -> Self {
8 Self {
9 seq: seq.to_string(),
10 }
11 }
12}
13
14impl std::fmt::Display for UnescapeError {
15 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
16 write!(f, "invalid unescape sequence {}", self.seq)
17 }
18}
19
20impl std::error::Error for UnescapeError {}
21
22pub(super) fn escape(unescaped: &str) -> String {
23 if unescaped.find('\\').is_none()
24 && unescaped.find('"').is_none()
25 && unescaped.find('\n').is_none()
26 && unescaped.find('\r').is_none()
27 && unescaped.find('\t').is_none()
28 {
29 return unescaped.to_string();
30 }
31 let mut escaped = String::new();
32 for c in unescaped.to_string().chars() {
33 match c {
34 '\\' => {
35 escaped.push_str("\\\\");
36 }
37 '"' => {
38 escaped.push_str("\\\"");
39 }
40 '\n' => {
41 escaped.push_str("\\n");
42 }
43 '\r' => {
44 escaped.push_str("\\r");
45 }
46 '\t' => {
47 escaped.push_str("\\t");
48 }
49 _ => {
50 escaped.push(c);
51 }
52 }
53 }
54 escaped
55}
56
57pub(super) fn unescape(escaped: &str) -> Result<String, UnescapeError> {
58 let first_backslash = escaped.find('\\');
59 if let Some(i) = first_backslash {
60 let mut unescaped = String::from(&escaped[0..i]);
61 let escaped: Vec<char> = escaped[i..].to_string().chars().collect();
62 let mut i = 0;
63 while i < escaped.len() {
64 if escaped[i] != '\\' || i == escaped.len() - 1 {
65 unescaped.push(escaped[i]);
66 } else {
67 i += 1;
68 match escaped[i] {
69 '\\' => {
70 unescaped.push('\\');
71 }
72 'n' => {
73 unescaped.push('\n');
74 }
75 'r' => {
76 unescaped.push('\r');
77 }
78 't' => {
79 unescaped.push('\t');
80 }
81 '"' => {
82 unescaped.push('"');
83 }
84 _ => {
85 return Err(UnescapeError::new(escaped[i]));
86 }
87 }
88 }
89 i += 1;
90 }
91 Ok(unescaped)
92 } else {
93 Ok(escaped.to_string())
94 }
95}
96
97#[cfg(test)]
98mod test {
99 #[test]
100 fn test_escape() {
101 use crate::po_file::escape::escape;
102 let unescaped = "1\n2\n3\n";
103 let expected = r"1\n2\n3\n";
104 assert_eq!(escape(unescaped), expected);
105 }
106
107 #[test]
108 fn test_unescape() {
109 use crate::po_file::escape::unescape;
110 let raw = r"1\n2\n3\n";
111 let expected = "1\n2\n3\n";
112 assert_eq!(unescape(raw).unwrap(), expected);
113 }
114}
115