1use nom::{
4 error::{Error, ErrorKind},
5 IResult,
6};
7use std::borrow::Cow;
8
9pub trait Decode {
10 fn as_str(&self) -> &str;
11
12 fn decode(&self) -> Result<Cow<str>, Box<dyn std::error::Error + '_>> {
13 match self.as_str() {
14 "amp" => Ok(Cow::Borrowed("&")),
15 "lt" => Ok(Cow::Borrowed("<")),
16 "gt" => Ok(Cow::Borrowed(">")),
17 "quot" => Ok(Cow::Borrowed("\"")),
18 "apos" => Ok(Cow::Borrowed("'")),
19 s if s.starts_with("&#x") && s.ends_with(';') => {
20 let code = &s[3..s.len() - 1]; match self.decode_hex(code) {
22 Ok((_, cow)) => Ok(cow),
23 Err(e) => Err(Box::new(e)),
24 }
25 }
26 s if s.starts_with("&#") && s.ends_with(';') => {
27 let code = &s[2..s.len() - 1]; match self.decode_digit(code) {
29 Ok((_, cow)) => Ok(cow),
30 Err(e) => Err(Box::new(e)),
31 }
32 }
33 _ => Err(Box::new(std::io::Error::new(
34 std::io::ErrorKind::Other,
35 "Failed to decode",
36 ))),
37 }
38 }
39
40 fn decode_hex(&self, code: &str) -> IResult<&str, Cow<str>, Error<&str>> {
41 match u32::from_str_radix(code, 16) {
42 Ok(n) => match char::from_u32(n) {
43 Some(c) => Ok((self.as_str(), Cow::Owned(c.to_string()))),
44 None => Err(nom::Err::Error(Error::new(
45 self.as_str(),
46 ErrorKind::MapRes,
47 ))),
48 },
49 Err(_) => Err(nom::Err::Error(Error::new(
50 self.as_str(),
51 ErrorKind::MapRes,
52 ))),
53 }
54 }
55
56 fn decode_digit(&self, code: &str) -> IResult<&str, Cow<str>, Error<&str>> {
57 match code.parse::<u32>() {
58 Ok(n) => match char::from_u32(n) {
59 Some(c) => Ok((self.as_str(), Cow::Owned(c.to_string()))),
60 None => Err(nom::Err::Error(Error::new(
61 self.as_str(),
62 ErrorKind::MapRes,
63 ))),
64 },
65 Err(_) => Err(nom::Err::Error(Error::new(
66 self.as_str(),
67 ErrorKind::MapRes,
68 ))),
69 }
70 }
71}
72
73impl Decode for String {
74 fn as_str(&self) -> &str {
75 self.as_ref()
76 }
77}
78
79impl Decode for &str {
80 fn as_str(&self) -> &str {
81 self
82 }
83}
84
85pub trait Encode {
86 fn as_str(&self) -> &str;
87
88 fn encode(&self) -> Result<String, Box<dyn std::error::Error + '_>> {
89 match self.as_str() {
90 "&" => Ok("amp".to_string()),
91 "<" => Ok("lt".to_string()),
92 ">" => Ok("gt".to_string()),
93 "\"" => Ok("quot".to_string()),
94 "'" => Ok("apos".to_string()),
95 _ => Err(Box::new(std::io::Error::new(
96 std::io::ErrorKind::Other,
97 "Unsupported character for encoding",
98 ))),
99 }
100 }
101
102 fn encode_hex(&self, ch: char) -> String {
103 format!("&#x{:X};", ch as u32)
104 }
105
106 fn encode_digit(&self, ch: char) -> String {
107 format!("&#{};", ch as u32)
108 }
109}
110
111impl Encode for String {
112 fn as_str(&self) -> &str {
113 self.as_ref()
114 }
115}
116
117impl Encode for &str {
118 fn as_str(&self) -> &str {
119 self
120 }
121}