1#[cfg(test)]
24mod tests;
25
26use nuts_backend::{Binary, IdSize};
27use std::convert::TryInto;
28use std::fmt;
29use std::path::{Path, PathBuf};
30use std::str::FromStr;
31
32use crate::error::{Error, Result};
33
34#[cfg(test)]
35fn rand_bytes() -> Result<[u8; SIZE]> {
36 Ok([
37 0xdb, 0x3d, 0x05, 0x23, 0xd4, 0x50, 0x75, 0x30, 0xe8, 0x6d, 0xf9, 0x6a, 0x1b, 0x76, 0xaa,
38 0x0c,
39 ])
40}
41
42#[cfg(not(test))]
43fn rand_bytes() -> Result<[u8; SIZE]> {
44 let mut buf = [0; SIZE];
45
46 getrandom::getrandom(&mut buf).map_err(Into::<std::io::Error>::into)?;
50
51 Ok(buf)
52}
53
54const SIZE: usize = 16;
55const HEX: [char; SIZE] = [
56 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
57];
58
59#[derive(Clone, PartialEq)]
67pub struct Id([u8; SIZE]);
68
69impl Binary for Id {
70 fn from_bytes(bytes: &[u8]) -> Option<Id> {
71 match bytes.try_into() {
72 Ok(buf) => Some(Id(buf)),
73 Err(_) => None,
74 }
75 }
76
77 fn as_bytes(&self) -> Vec<u8> {
78 self.0.to_vec()
79 }
80}
81
82impl IdSize for Id {
83 fn size() -> usize {
84 SIZE
85 }
86}
87
88impl Id {
89 pub(crate) fn generate() -> Result<Id> {
90 rand_bytes().map(Id)
91 }
92
93 pub(crate) fn min() -> Id {
94 Id([u8::MIN; SIZE])
95 }
96
97 fn as_hex(&self) -> String {
98 let mut target = String::with_capacity(2 * SIZE);
99
100 for b in self.0.iter() {
101 target.push(HEX[(*b as usize >> 4) & 0x0f]);
102 target.push(HEX[(*b as usize) & 0x0f]);
103 }
104
105 target
106 }
107
108 pub(crate) fn to_pathbuf(&self, parent: &Path) -> PathBuf {
109 let hex = self.as_hex();
110 let mut path = PathBuf::new();
111 let mut pos = 0;
112
113 path.push(parent);
114
115 for _ in 0..2 {
116 path.push(&hex[pos..pos + 2]);
117 pos += 2;
118 }
119
120 path.push(&hex[pos..]);
121
122 path
123 }
124}
125
126impl fmt::Display for Id {
127 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
128 fmt.write_str(&self.as_hex())
129 }
130}
131
132impl fmt::Debug for Id {
133 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
134 fmt.debug_tuple("Id").field(&self.to_string()).finish()
135 }
136}
137
138impl FromStr for Id {
139 type Err = Error;
140
141 fn from_str(s: &str) -> Result<Self> {
142 if s.len() != 2 * SIZE {
143 return Err(Error::InvalidId(s.to_string()));
144 }
145
146 let mut id = Id([0; SIZE]);
147
148 for (idx, c) in s.chars().enumerate() {
149 if let Some(n) = c.to_digit(16) {
150 let m = idx / 2;
151
152 if idx % 2 == 0 {
153 id.0[m] |= (n as u8 & 0x0f) << 4;
154 } else {
155 id.0[m] |= n as u8 & 0x0f;
156 }
157 } else {
158 return Err(Error::InvalidId(s.to_string()));
159 }
160 }
161
162 Ok(id)
163 }
164}