hallib_rs/
lib.rs

1use std::{num::{ParseFloatError, ParseIntError}, str::Utf8Error};
2
3#[derive(Clone, Copy, Hash, PartialEq)]
4pub struct KeyString {
5    inner: [u8;64],
6}
7
8impl std::fmt::Debug for KeyString {
9    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10        f.debug_struct("KeyString").field("inner", &self.as_str()).finish()
11    }
12}
13
14impl std::fmt::Display for KeyString {
15    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
16        let text = bytes_to_str(&self.inner).expect(&format!("A KeyString should always be valid utf8.\nThe KeyString that was just attempted to Display was:\n{:x?}", self.inner));
17        write!(f, "{}", text)
18    }   
19}
20
21impl Default for KeyString {
22    fn default() -> Self {
23        Self { inner: [0;64] }
24    }
25}
26
27/// Turns a &str into a KeyString. If the &str has more than 64 bytes, the last bytes will be cut.
28impl From<&str> for KeyString {
29    fn from(s: &str) -> Self {
30
31        let mut inner = [0u8;64];
32
33        let mut min = std::cmp::min(s.len(), 64);
34        inner[0..min].copy_from_slice(&s.as_bytes()[0..min]);
35
36        loop {
37            if min == 0 {break}
38            match std::str::from_utf8(&inner[0..min]) {
39                Ok(_) => break,
40                Err(_) => min -= 1,
41            }
42        }
43
44        KeyString {
45            inner
46        }
47
48    }
49}
50
51
52impl TryFrom<&[u8]> for KeyString {
53    type Error = Utf8Error;
54
55    fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
56        let mut inner = [0u8;64];
57
58        let min = std::cmp::min(s.len(), 64);
59        inner[0..min].copy_from_slice(&s[0..min]);
60
61        match std::str::from_utf8(&inner) {
62            Ok(_) => {
63                Ok(KeyString {inner})
64            },
65            Err(e) => Err(e),
66        }
67    }
68}
69
70impl Eq for KeyString {}
71
72impl Ord for KeyString {
73    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
74        self.as_str().cmp(other.as_str())
75    }
76}
77
78impl PartialOrd for KeyString {
79    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
80        Some(self.as_str().cmp(other.as_str()))
81    }
82}
83
84impl KeyString {
85
86    pub fn new() -> Self {
87        KeyString {
88            inner: [0u8; 64]
89        }
90    }
91
92    pub fn len(&self) -> usize {
93        let mut output = 0;
94        for byte in self.inner {
95            match byte {
96                0 => break,
97                _ => output += 1,
98            }
99        }
100        output
101    }
102
103    pub fn push(&mut self, s: &str) {
104
105        if self.len() + s.len() > 64 {
106            return
107        }
108
109        let mut end_index = 0;
110        for (index, byte) in self.inner.iter().enumerate() {
111            if byte == &0 {
112                end_index = index+1;
113            }
114        }
115
116        for (index, byte) in s.as_bytes().iter().enumerate() {
117            self.inner[index+end_index] = *byte;
118        }
119
120    }
121
122    pub fn as_str(&self) -> &str {
123        // This is safe since an enforced invariant of KeyString is that it is utf8
124        unsafe { std::str::from_utf8_unchecked(&self.inner[0..self.len()]) }
125    }
126
127    pub fn as_bytes(&self) -> &[u8] {
128        &self.inner[0..self.len()]
129    }
130
131    pub fn raw(&self) -> &[u8] {
132        &self.inner
133    }
134
135    /// These functions may panic and should only be called if you are certain that the KeyString contains a valid number
136    pub fn to_i32(&self) -> i32 {
137        self.as_str().parse::<i32>().unwrap()
138    }
139
140    /// These functions may panic and should only be called if you are certain that the KeyString contains a valid number
141    pub fn to_f32(&self) -> f32 {
142        self.as_str().parse::<f32>().unwrap()
143    }
144
145    pub fn to_i32_checked(&self) -> Result<i32, ParseIntError> {
146        self.as_str().parse::<i32>()
147    }
148
149    pub fn to_f32_checked(&self) -> Result<f32, ParseFloatError> {
150        self.as_str().parse::<f32>()
151    }
152
153}
154
155
156/// Removes the trailing 0 bytes from a str created from a byte buffer
157pub fn bytes_to_str(bytes: &[u8]) -> Result<&str, Utf8Error> {
158    let mut index: usize = 0;
159    let len = bytes.len();
160    let mut start: usize = 0;
161    
162    while index < len {
163        if bytes[index] != 0 {
164            break
165        }
166        index += 1;
167        start += 1;
168    }
169
170    if bytes.is_empty() {
171        return Ok("")
172    }
173
174    if start >= bytes.len()-1 {
175        return Ok("")
176    }
177
178    let mut stop: usize = start;
179    while index < len {
180        if bytes[index] == 0 {
181            break
182        }
183        index += 1;
184        stop += 1;
185    }
186
187    std::str::from_utf8(&bytes[start..stop])
188}
189
190
191
192pub fn add(left: usize, right: usize) -> usize {
193    left + right
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn it_works() {
202        let result = add(2, 2);
203        assert_eq!(result, 4);
204    }
205}