Skip to main content

ens_normalize/
utils.rs

1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct EnsError {
5    message: String,
6}
7
8impl EnsError {
9    pub fn new(message: impl Into<String>) -> Self {
10        Self {
11            message: message.into(),
12        }
13    }
14
15    pub fn message(&self) -> &str {
16        &self.message
17    }
18}
19
20impl fmt::Display for EnsError {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.write_str(&self.message)
23    }
24}
25
26impl std::error::Error for EnsError {}
27
28pub type Result<T> = std::result::Result<T, EnsError>;
29
30pub fn hex_cp(cp: u32) -> String {
31    let mut s = format!("{cp:X}");
32    if s.len() < 2 {
33        s.insert(0, '0');
34    }
35    s
36}
37
38pub fn quote_cp(cp: u32) -> String {
39    format!("{{{}}}", hex_cp(cp))
40}
41
42pub fn explode_cp(s: &str) -> Vec<u32> {
43    s.chars().map(|c| c as u32).collect()
44}
45
46pub fn str_from_cps(cps: &[u32]) -> Result<String> {
47    let mut s = String::new();
48    for &cp in cps {
49        let ch = char::from_u32(cp)
50            .ok_or_else(|| EnsError::new(format!("invalid code point: {}", quote_cp(cp))))?;
51        s.push(ch);
52    }
53    Ok(s)
54}
55
56pub(crate) fn compare_arrays(a: &[u32], b: &[u32]) -> i32 {
57    let mut c = a.len() as i32 - b.len() as i32;
58    let mut i = 0;
59    while c == 0 && i < a.len() {
60        c = a[i] as i32 - b[i] as i32;
61        i += 1;
62    }
63    c
64}
65
66pub(crate) fn array_replace(v: &mut [u32], a: u32, b: u32) {
67    for cp in v {
68        if *cp == a {
69            *cp = b;
70        }
71    }
72}
73
74pub(crate) fn bidi_qq(s: &str) -> String {
75    format!("\"{s}\"\u{200E}")
76}
77
78pub fn safe_str_from_cps(cps: &[u32], max: Option<usize>) -> String {
79    safe_str_from_cps_with(cps, max, &quote_cp)
80}
81
82pub(crate) fn safe_str_from_cps_with(
83    cps: &[u32],
84    max: Option<usize>,
85    quoter: &dyn Fn(u32) -> String,
86) -> String {
87    use crate::spec::{is_combining_mark, should_escape};
88
89    let max = max.unwrap_or(usize::MAX);
90    let mut working: Vec<u32> = if cps.len() > max {
91        let half = max >> 1;
92        let mut v = Vec::with_capacity(half * 2 + 1);
93        v.extend_from_slice(&cps[..half]);
94        v.push(0x2026);
95        v.extend_from_slice(&cps[cps.len().saturating_sub(half)..]);
96        v
97    } else {
98        cps.to_vec()
99    };
100
101    let mut buf = String::new();
102    if working
103        .first()
104        .copied()
105        .is_some_and(|cp| is_combining_mark(cp, false))
106    {
107        buf.push('\u{25CC}');
108    }
109
110    let mut prev = 0;
111    for i in 0..working.len() {
112        let cp = working[i];
113        if should_escape(cp) {
114            if let Ok(s) = str_from_cps(&working[prev..i]) {
115                buf.push_str(&s);
116            }
117            buf.push_str(&quoter(cp));
118            prev = i + 1;
119        }
120    }
121    if let Ok(s) = str_from_cps(&working[prev..]) {
122        buf.push_str(&s);
123    }
124    working.clear();
125    buf
126}