1#[must_use]
20pub fn precision_from_str(s: &str) -> u8 {
21 let s = s.trim().to_ascii_lowercase();
22
23 if s.contains("e-") {
25 return s.split("e-").last().unwrap().parse::<u8>().unwrap();
26 }
27
28 if let Some((_, decimal_part)) = s.split_once('.') {
30 decimal_part.len() as u8
31 } else {
32 0
33 }
34}
35
36#[must_use]
39pub fn min_increment_precision_from_str(s: &str) -> u8 {
40 let s = s.trim().to_ascii_lowercase();
41
42 if let Some(pos) = s.find('e') {
44 if s[pos + 1..].starts_with('-') {
45 return s[pos + 2..].parse::<u8>().unwrap_or(0);
46 }
47 }
48
49 if let Some(dot_pos) = s.find('.') {
51 let decimal_part = &s[dot_pos + 1..];
52 if decimal_part.chars().any(|c| c != '0') {
53 let trimmed_len = decimal_part.trim_end_matches('0').len();
54 return trimmed_len as u8;
55 } else {
56 return decimal_part.len() as u8;
57 }
58 }
59
60 0
61}
62
63pub fn bytes_to_usize(bytes: &[u8]) -> anyhow::Result<usize> {
69 if bytes.len() >= std::mem::size_of::<usize>() {
71 let mut buffer = [0u8; std::mem::size_of::<usize>()];
72 buffer.copy_from_slice(&bytes[..std::mem::size_of::<usize>()]);
73
74 Ok(usize::from_le_bytes(buffer))
75 } else {
76 Err(anyhow::anyhow!("Not enough bytes to represent a `usize`"))
77 }
78}
79
80#[cfg(test)]
84mod tests {
85 use rstest::rstest;
86
87 use super::*;
88
89 #[rstest]
90 #[case("", 0)]
91 #[case("0", 0)]
92 #[case("1.0", 1)]
93 #[case("1.00", 2)]
94 #[case("1.23456789", 8)]
95 #[case("123456.789101112", 9)]
96 #[case("0.000000001", 9)]
97 #[case("1e-1", 1)]
98 #[case("1e-2", 2)]
99 #[case("1e-3", 3)]
100 #[case("1e8", 0)]
101 #[case("-1.23", 2)]
102 #[case("-1e-2", 2)]
103 #[case("1E-2", 2)]
104 #[case(" 1.23", 2)]
105 #[case("1.23 ", 2)]
106 fn test_precision_from_str(#[case] s: &str, #[case] expected: u8) {
107 let result = precision_from_str(s);
108 assert_eq!(result, expected);
109 }
110
111 #[rstest]
112 #[case("", 0)]
113 #[case("0", 0)]
114 #[case("1.0", 1)]
115 #[case("1.00", 2)]
116 #[case("1.23456789", 8)]
117 #[case("123456.789101112", 9)]
118 #[case("0.000000001", 9)]
119 #[case("1e-1", 1)]
120 #[case("1e-2", 2)]
121 #[case("1e-3", 3)]
122 #[case("1e8", 0)]
123 #[case("-1.23", 2)]
124 #[case("-1e-2", 2)]
125 #[case("1E-2", 2)]
126 #[case(" 1.23", 2)]
127 #[case("1.23 ", 2)]
128 #[case("1.010", 2)]
129 #[case("1.00100", 3)]
130 #[case("0.0001000", 4)]
131 #[case("1.000000000", 9)]
132 fn test_min_increment_precision_from_str(#[case] s: &str, #[case] expected: u8) {
133 let result = min_increment_precision_from_str(s);
134 assert_eq!(result, expected);
135 }
136
137 #[rstest]
138 fn test_bytes_to_usize_empty() {
139 let payload: Vec<u8> = vec![];
140 let result = bytes_to_usize(&payload);
141 assert!(result.is_err());
142 assert_eq!(
143 result.err().unwrap().to_string(),
144 "Not enough bytes to represent a `usize`"
145 );
146 }
147
148 #[rstest]
149 fn test_bytes_to_usize_invalid() {
150 let payload: Vec<u8> = vec![0x01, 0x02, 0x03];
151 let result = bytes_to_usize(&payload);
152 assert!(result.is_err());
153 assert_eq!(
154 result.err().unwrap().to_string(),
155 "Not enough bytes to represent a `usize`"
156 );
157 }
158
159 #[rstest]
160 fn test_bytes_to_usize_valid() {
161 let payload: Vec<u8> = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
162 let result = bytes_to_usize(&payload).unwrap();
163 assert_eq!(result, 0x0807_0605_0403_0201);
164 assert_eq!(result, 578_437_695_752_307_201);
165 }
166}