Skip to main content

uxn_tal_defined/
lib.rs

1pub mod consts;
2pub mod v1;
3pub use v1::*;
4pub mod emu_buxn;
5pub mod emu_cuxn;
6pub mod emu_uxn;
7
8/// Decode percent-encoding. If the decoded bytes aren't valid UTF-8,
9/// return the original string unchanged.
10///
11/// Examples:
12/// - "Hello%20World%21" => "Hello World!"
13/// - "%E2%9C%93" => "✓"
14/// - "%ZZ" (malformed) => "%ZZ" (unchanged, because decoding would be invalid)
15pub fn percent_decode_or_original(s: &str) -> String {
16    // Fast path: nothing to do
17    if !s.as_bytes().contains(&b'%') {
18        return s.to_string();
19    }
20
21    // Decode %XX into raw bytes; copy other bytes as-is.
22    let mut out = Vec::with_capacity(s.len());
23    let bytes = s.as_bytes();
24    let mut i = 0;
25    while i < bytes.len() {
26        if bytes[i] == b'%' && i + 2 < bytes.len() {
27            if let (Some(h), Some(l)) = (hex_val(bytes[i + 1]), hex_val(bytes[i + 2])) {
28                out.push((h << 4) | l);
29                i += 3;
30                continue;
31            }
32            // Malformed escape: keep '%' literally
33            out.push(b'%');
34            i += 1;
35        } else {
36            out.push(bytes[i]);
37            i += 1;
38        }
39    }
40
41    // Try UTF-8; on failure, return the original unchanged.
42    match std::str::from_utf8(&out) {
43        Ok(decoded) => decoded.to_string(),
44        Err(_) => s.to_string(),
45    }
46}
47
48fn hex_val(b: u8) -> Option<u8> {
49    match b {
50        b'0'..=b'9' => Some(b - b'0'),
51        b'a'..=b'f' => Some(b - b'a' + 10),
52        b'A'..=b'F' => Some(b - b'A' + 10),
53        _ => None,
54    }
55}