sim_kernel/catalog/
key.rs1use crate::{Error, Result, Symbol};
2
3pub fn catalog_key(namespace: &str, parts: &[&str]) -> Symbol {
5 let mut key = String::new();
6 push_escaped(&mut key, namespace);
7 for part in parts {
8 key.push('/');
9 push_escaped(&mut key, part);
10 }
11 Symbol::new(key)
12}
13
14pub fn split_catalog_key(symbol: &Symbol) -> Result<Vec<String>> {
16 symbol
17 .as_qualified_str()
18 .split('/')
19 .map(decode_part)
20 .collect()
21}
22
23fn push_escaped(out: &mut String, text: &str) {
24 for byte in text.bytes() {
25 match byte {
26 b'%' => out.push_str("%25"),
27 b'/' => out.push_str("%2F"),
28 0x00..=0x1f | 0x7f..=0xff => push_hex_escape(out, byte),
29 _ => out.push(char::from(byte)),
30 }
31 }
32}
33
34fn push_hex_escape(out: &mut String, byte: u8) {
35 const HEX: &[u8; 16] = b"0123456789ABCDEF";
36 out.push('%');
37 out.push(char::from(HEX[usize::from(byte >> 4)]));
38 out.push(char::from(HEX[usize::from(byte & 0x0f)]));
39}
40
41fn decode_part(text: &str) -> Result<String> {
42 let bytes = text.as_bytes();
43 let mut decoded = Vec::with_capacity(bytes.len());
44 let mut index = 0;
45 while index < bytes.len() {
46 match bytes[index] {
47 b'%' => {
48 let hi = bytes
49 .get(index + 1)
50 .copied()
51 .and_then(hex_value)
52 .ok_or_else(malformed_escape)?;
53 let lo = bytes
54 .get(index + 2)
55 .copied()
56 .and_then(hex_value)
57 .ok_or_else(malformed_escape)?;
58 decoded.push((hi << 4) | lo);
59 index += 3;
60 }
61 byte @ (0x00..=0x1f | 0x7f..=0xff) => {
62 return Err(catalog_key_schema_error(format!(
63 "catalog key contains unescaped byte 0x{byte:02X}"
64 )));
65 }
66 byte => {
67 decoded.push(byte);
68 index += 1;
69 }
70 }
71 }
72 String::from_utf8(decoded)
73 .map_err(|_| catalog_key_schema_error("catalog key escape is not valid UTF-8"))
74}
75
76fn hex_value(byte: u8) -> Option<u8> {
77 match byte {
78 b'0'..=b'9' => Some(byte - b'0'),
79 b'a'..=b'f' => Some(byte - b'a' + 10),
80 b'A'..=b'F' => Some(byte - b'A' + 10),
81 _ => None,
82 }
83}
84
85fn malformed_escape() -> Error {
86 catalog_key_schema_error("malformed catalog key escape")
87}
88
89fn catalog_key_schema_error(message: impl Into<String>) -> Error {
90 Error::CatalogSchema {
91 table: Symbol::new("catalog/key"),
92 message: message.into(),
93 }
94}