1 | #[derive (Debug)] |
2 | pub(crate) struct UnescapeError { |
3 | seq: String, |
4 | } |
5 | |
6 | impl UnescapeError { |
7 | fn new(seq: char) -> Self { |
8 | Self { |
9 | seq: seq.to_string(), |
10 | } |
11 | } |
12 | } |
13 | |
14 | impl 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 | |
20 | impl std::error::Error for UnescapeError {} |
21 | |
22 | pub(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 | |
57 | pub(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)] |
98 | mod 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 | |