nom_xml/
transcode.rs

1// transcode.rs
2
3use 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]; // slice to get content inside &#x...;
21                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]; // slice to get content inside &#...;
28                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}