1mod error;
9
10pub use error::{Error, Result};
11use std::path::MAIN_SEPARATOR;
12
13use fast32::base32::CROCKFORD_LOWER;
14use hipstr::HipStr;
15
16pub const DEPTH1: u8 = b'_'; pub const DEPTH2: u8 = b'-'; pub const DEPTH3: u8 = b'~'; const SEP: u8 = MAIN_SEPARATOR as u8;
22
23#[inline]
26pub fn encode(prefix: impl AsRef<str>, id: u64) -> HipStr<'static> {
27 let prefix = prefix.as_ref();
28 let encoded = CROCKFORD_LOWER.encode_u64(id);
29 let enc = encoded.as_bytes();
30 let len = enc.len();
31
32 let cap = prefix.len() + (len + 3).max(6);
33 let mut buf = Vec::with_capacity(cap);
34
35 buf.extend_from_slice(prefix.as_bytes());
36 buf.push(SEP);
37
38 match len {
39 0..=2 => {
40 buf.extend_from_slice(enc);
41 buf.push(DEPTH1);
42 }
43 3 => {
44 buf.extend_from_slice(&enc[1..3]);
45 buf.push(SEP);
46 buf.push(enc[0]);
47 buf.push(DEPTH2);
48 }
49 4 => {
50 buf.extend_from_slice(&enc[2..4]);
51 buf.push(SEP);
52 buf.extend_from_slice(&enc[..2]);
53 buf.push(DEPTH3);
54 }
55 5 => {
56 buf.extend_from_slice(&enc[3..5]);
57 buf.push(SEP);
58 buf.extend_from_slice(&enc[1..3]);
59 buf.push(SEP);
60 buf.push(enc[0]);
61 }
62 _ => {
63 buf.extend_from_slice(&enc[len - 2..]);
64 buf.push(SEP);
65 buf.extend_from_slice(&enc[len - 4..len - 2]);
66 buf.push(SEP);
67 buf.extend_from_slice(&enc[..len - 4]);
68 }
69 }
70
71 unsafe { HipStr::from_utf8_unchecked(buf.into()) }
73}
74
75pub fn decode(path: impl AsRef<str>) -> Result<u64> {
77 let path_str = path.as_ref();
78 let mut iter = path_str.rsplit(MAIN_SEPARATOR);
79 let last_part = iter.next().ok_or_else(|| Error::InvalidPath(path_str.into()))?;
80
81 let mut buf = [0u8; 16];
82 let mut pos = 0;
83
84 let mut push = |src: &[u8]| -> Result<()> {
85 let len = src.len();
86 if pos + len > buf.len() {
87 return Err(Error::InvalidPath(path_str.into()));
88 }
89 buf[pos..pos + len].copy_from_slice(src);
90 pos += len;
91 Ok(())
92 };
93
94 if let Some(&suffix) = last_part.as_bytes().last() {
95 match suffix {
96 DEPTH1 => {
97 let name = &last_part[..last_part.len() - 1];
98 push(name.as_bytes())?;
99 }
100 DEPTH2 | DEPTH3 => {
101 let name = &last_part[..last_part.len() - 1];
102 let d2 =
103 iter.next().ok_or_else(|| Error::InvalidPath(path_str.into()))?;
104 if d2.len() != 2 {
105 return Err(Error::InvalidPath(path_str.into()));
106 }
107 push(name.as_bytes())?;
108 push(d2.as_bytes())?;
109 }
110 _ => {
111 let d2 =
112 iter.next().ok_or_else(|| Error::InvalidPath(path_str.into()))?;
113 let d1 =
114 iter.next().ok_or_else(|| Error::InvalidPath(path_str.into()))?;
115 if d2.len() != 2 || d1.len() != 2 {
116 return Err(Error::InvalidPath(path_str.into()));
117 }
118 push(last_part.as_bytes())?;
119 push(d2.as_bytes())?;
120 push(d1.as_bytes())?;
121 }
122 }
123 } else {
124 return Err(Error::InvalidPath(path_str.into()));
125 }
126
127 CROCKFORD_LOWER
128 .decode_u64(&buf[..pos])
129 .map_err(|_| Error::DecodeFailed(path_str.into()))
130}