1use std::collections::HashSet;
4
5use serde_json::{Number, Value};
6
7use crate::functions::{Function, number_value};
8use crate::interpreter::SearchResult;
9use crate::registry::register_if_enabled;
10use crate::{Context, Runtime, arg, defn};
11
12const DECIMAL_UNITS: &[(&str, f64)] = &[
14 ("PB", 1e15),
15 ("TB", 1e12),
16 ("GB", 1e9),
17 ("MB", 1e6),
18 ("KB", 1e3),
19 ("B", 1.0),
20];
21
22const BINARY_UNITS: &[(&str, f64)] = &[
24 ("PiB", 1125899906842624.0),
25 ("TiB", 1099511627776.0),
26 ("GiB", 1073741824.0),
27 ("MiB", 1048576.0),
28 ("KiB", 1024.0),
29 ("B", 1.0),
30];
31
32defn!(ParseBytesFn, vec![arg!(string)], None);
33
34impl Function for ParseBytesFn {
35 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
36 self.signature.validate(args, ctx)?;
37
38 let s = args[0]
39 .as_str()
40 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected string"))?;
41
42 match parse_bytes_str(s) {
43 Some(bytes) => Ok(number_value(bytes)),
44 None => Ok(Value::Null),
45 }
46 }
47}
48
49defn!(FormatBytesFn, vec![arg!(number)], None);
50
51impl Function for FormatBytesFn {
52 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
53 self.signature.validate(args, ctx)?;
54
55 let bytes = args[0]
56 .as_f64()
57 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected number"))?;
58
59 let formatted = format_bytes_with_units(bytes, DECIMAL_UNITS);
60 Ok(Value::String(formatted))
61 }
62}
63
64defn!(FormatBytesBinaryFn, vec![arg!(number)], None);
65
66impl Function for FormatBytesBinaryFn {
67 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
68 self.signature.validate(args, ctx)?;
69
70 let bytes = args[0]
71 .as_f64()
72 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected number"))?;
73
74 let formatted = format_bytes_with_units(bytes, BINARY_UNITS);
75 Ok(Value::String(formatted))
76 }
77}
78
79defn!(BitAndFn, vec![arg!(number), arg!(number)], None);
80
81impl Function for BitAndFn {
82 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
83 self.signature.validate(args, ctx)?;
84
85 let a = args[0]
86 .as_f64()
87 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
88 as i64;
89
90 let b = args[1]
91 .as_f64()
92 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
93 as i64;
94
95 Ok(Value::Number(Number::from(a & b)))
96 }
97}
98
99defn!(BitOrFn, vec![arg!(number), arg!(number)], None);
100
101impl Function for BitOrFn {
102 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
103 self.signature.validate(args, ctx)?;
104
105 let a = args[0]
106 .as_f64()
107 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
108 as i64;
109
110 let b = args[1]
111 .as_f64()
112 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
113 as i64;
114
115 Ok(Value::Number(Number::from(a | b)))
116 }
117}
118
119defn!(BitXorFn, vec![arg!(number), arg!(number)], None);
120
121impl Function for BitXorFn {
122 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
123 self.signature.validate(args, ctx)?;
124
125 let a = args[0]
126 .as_f64()
127 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
128 as i64;
129
130 let b = args[1]
131 .as_f64()
132 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
133 as i64;
134
135 Ok(Value::Number(Number::from(a ^ b)))
136 }
137}
138
139defn!(BitNotFn, vec![arg!(number)], None);
140
141impl Function for BitNotFn {
142 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
143 self.signature.validate(args, ctx)?;
144
145 let a = args[0]
146 .as_f64()
147 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
148 as i64;
149
150 Ok(Value::Number(Number::from(!a)))
151 }
152}
153
154defn!(BitShiftLeftFn, vec![arg!(number), arg!(number)], None);
155
156impl Function for BitShiftLeftFn {
157 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
158 self.signature.validate(args, ctx)?;
159
160 let a = args[0]
161 .as_f64()
162 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
163 as i64;
164
165 let n = args[1]
166 .as_f64()
167 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected non-negative integer"))?
168 as u32;
169
170 Ok(Value::Number(Number::from(a << n)))
171 }
172}
173
174defn!(BitShiftRightFn, vec![arg!(number), arg!(number)], None);
175
176impl Function for BitShiftRightFn {
177 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
178 self.signature.validate(args, ctx)?;
179
180 let a = args[0]
181 .as_f64()
182 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected integer"))?
183 as i64;
184
185 let n = args[1]
186 .as_f64()
187 .ok_or_else(|| crate::functions::custom_error(ctx, "Expected non-negative integer"))?
188 as u32;
189
190 Ok(Value::Number(Number::from(a >> n)))
191 }
192}
193
194fn parse_bytes_str(s: &str) -> Option<f64> {
198 let s = s.trim();
199 if s.is_empty() {
200 return None;
201 }
202
203 let mut num_end = 0;
204 let chars: Vec<char> = s.chars().collect();
205
206 for (i, c) in chars.iter().enumerate() {
207 if c.is_ascii_digit() || *c == '.' || *c == '-' || *c == '+' {
208 num_end = i + 1;
209 } else if !c.is_whitespace() {
210 break;
211 }
212 }
213
214 if num_end == 0 {
215 return None;
216 }
217
218 let num_str: String = chars[..num_end].iter().collect();
219 let num: f64 = num_str.trim().parse().ok()?;
220
221 let unit: String = chars[num_end..].iter().collect();
222 let unit = unit.trim().to_uppercase();
223
224 if unit.is_empty() || unit == "B" || unit == "BYTES" || unit == "BYTE" {
225 return Some(num);
226 }
227
228 let multiplier = match unit.as_str() {
229 "PIB" | "PEBIBYTE" | "PEBIBYTES" => 1125899906842624.0,
230 "TIB" | "TEBIBYTE" | "TEBIBYTES" => 1099511627776.0,
231 "GIB" | "GIBIBYTE" | "GIBIBYTES" => 1073741824.0,
232 "MIB" | "MEBIBYTE" | "MEBIBYTES" => 1048576.0,
233 "KIB" | "KIBIBYTE" | "KIBIBYTES" => 1024.0,
234 "PB" | "PETABYTE" | "PETABYTES" => 1e15,
235 "TB" | "TERABYTE" | "TERABYTES" => 1e12,
236 "GB" | "GIGABYTE" | "GIGABYTES" => 1e9,
237 "MB" | "MEGABYTE" | "MEGABYTES" => 1e6,
238 "KB" | "KILOBYTE" | "KILOBYTES" => 1e3,
239 _ => return None,
240 };
241
242 Some(num * multiplier)
243}
244
245fn format_bytes_with_units(bytes: f64, units: &[(&str, f64)]) -> String {
247 if bytes == 0.0 {
248 return "0 B".to_string();
249 }
250
251 let abs_bytes = bytes.abs();
252
253 for (unit, threshold) in units {
254 if abs_bytes >= *threshold {
255 let value = bytes / threshold;
256 let formatted = format!("{:.2}", value);
257 let formatted = formatted.trim_end_matches('0').trim_end_matches('.');
258 return format!("{} {}", formatted, unit);
259 }
260 }
261
262 format!("{} B", bytes)
263}
264
265pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
267 register_if_enabled(
268 runtime,
269 "parse_bytes",
270 enabled,
271 Box::new(ParseBytesFn::new()),
272 );
273 register_if_enabled(
274 runtime,
275 "format_bytes",
276 enabled,
277 Box::new(FormatBytesFn::new()),
278 );
279 register_if_enabled(
280 runtime,
281 "format_bytes_binary",
282 enabled,
283 Box::new(FormatBytesBinaryFn::new()),
284 );
285 register_if_enabled(runtime, "bit_and", enabled, Box::new(BitAndFn::new()));
286 register_if_enabled(runtime, "bit_or", enabled, Box::new(BitOrFn::new()));
287 register_if_enabled(runtime, "bit_xor", enabled, Box::new(BitXorFn::new()));
288 register_if_enabled(runtime, "bit_not", enabled, Box::new(BitNotFn::new()));
289 register_if_enabled(
290 runtime,
291 "bit_shift_left",
292 enabled,
293 Box::new(BitShiftLeftFn::new()),
294 );
295 register_if_enabled(
296 runtime,
297 "bit_shift_right",
298 enabled,
299 Box::new(BitShiftRightFn::new()),
300 );
301}