edit_xml/
utils.rs

1#![allow(clippy::wrong_self_convention)]
2pub mod encoding;
3use core::str;
4use std::ops::Deref;
5
6use quick_xml::{
7    escape::resolve_predefined_entity,
8    events::{BytesPI, BytesText},
9    name::QName,
10};
11use tracing::debug;
12
13use crate::EditXMLError;
14#[cfg(not(feature = "ahash"))]
15pub type HashMap<K, V> = std::collections::HashMap<K, V>;
16#[cfg(feature = "ahash")]
17pub type HashMap<K, V> = ahash::AHashMap<K, V>;
18
19/// Trait for converting quick-xml types to string
20pub trait XMLStringUtils {
21    /// Escapes non-ascii characters into their escape sequences
22    fn escape_ascii_into_string(&self) -> Result<String, EditXMLError>;
23    /// Converts the type into a string
24    fn into_string(&self) -> Result<String, EditXMLError>;
25    /// Unescapes the content of the type into a string
26    fn unescape_to_string(&self) -> Result<String, EditXMLError> {
27        let value = self.into_string()?;
28        debug!("Unescaping: {}", value);
29        let unescape =
30            crate::utils::encoding::unescape_with(value.as_str(), resolve_predefined_entity)?;
31        debug!("Unescaped: {}", unescape);
32        Ok(unescape.into_owned())
33    }
34}
35
36impl XMLStringUtils for BytesText<'_> {
37    fn escape_ascii_into_string(&self) -> Result<String, EditXMLError> {
38        Ok(self.escape_ascii().to_string())
39    }
40
41    fn into_string(&self) -> Result<String, EditXMLError> {
42        String::from_utf8(self.to_vec()).map_err(EditXMLError::from)
43    }
44    fn unescape_to_string(&self) -> Result<String, EditXMLError> {
45        bytes_to_unescaped_string(self.deref())
46    }
47}
48impl XMLStringUtils for QName<'_> {
49    fn escape_ascii_into_string(&self) -> Result<String, EditXMLError> {
50        self.into_string()
51    }
52
53    fn into_string(&self) -> Result<String, EditXMLError> {
54        String::from_utf8(self.0.to_vec()).map_err(EditXMLError::from)
55    }
56
57    fn unescape_to_string(&self) -> Result<String, EditXMLError> {
58        bytes_to_unescaped_string(self.0)
59    }
60}
61impl XMLStringUtils for BytesPI<'_> {
62    fn escape_ascii_into_string(&self) -> Result<String, EditXMLError> {
63        self.into_string()
64    }
65
66    fn into_string(&self) -> Result<String, EditXMLError> {
67        Ok(String::from_utf8(self.to_vec())?)
68    }
69
70    fn unescape_to_string(&self) -> Result<String, EditXMLError> {
71        bytes_to_unescaped_string(self.content())
72    }
73}
74pub(crate) fn bytes_to_unescaped_string(cow: &[u8]) -> Result<String, EditXMLError> {
75    let value = str::from_utf8(cow).map_err(EditXMLError::from)?;
76
77    let unescape = crate::utils::encoding::unescape_with(value, resolve_predefined_entity)?;
78    Ok(unescape.into_owned())
79}
80#[cfg(test)]
81pub mod tests {
82    use std::path::PathBuf;
83
84    use std::sync::Once;
85    use tracing::{debug, info};
86    use tracing_subscriber::fmt;
87    use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
88    pub fn setup_logger() {
89        static INIT: Once = Once::new();
90        INIT.call_once(|| {
91            let stdout_log = fmt::layer().pretty();
92            tracing_subscriber::registry().with(stdout_log).init();
93        });
94        info!("Logger initialized");
95        debug!("Logger initialized");
96    }
97    pub fn test_dir() -> std::path::PathBuf {
98        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests")
99    }
100    pub fn documents_dir() -> std::path::PathBuf {
101        test_dir().join("documents")
102    }
103}