Skip to main content

tanzim_validate/
bytesize.rs

1use crate::error::{Error, ErrorKind};
2use crate::{Meta, Validator};
3use tanzim_value::{Value, ValueType};
4
5/// (`bytesize` feature) Accepts a human byte-size string (e.g. `"10MB"`, `"1GiB"`) and coerces it to an integer
6/// number of bytes.
7#[derive(Debug, Clone, Default)]
8pub struct ByteSize {
9    meta: Meta,
10}
11
12impl ByteSize {
13    pub fn new() -> Self {
14        Self {
15            meta: Meta::default(),
16        }
17    }
18
19    /// Attach human-facing metadata (name, description, examples, default, output conversion).
20    pub fn with_meta(mut self, meta: Meta) -> Self {
21        self.meta = meta;
22        self
23    }
24}
25
26crate::impl_meta_methods!(ByteSize);
27
28impl Validator for ByteSize {
29    fn meta(&self) -> &Meta {
30        &self.meta
31    }
32
33    fn meta_mut(&mut self) -> &mut Meta {
34        &mut self.meta
35    }
36
37    fn check(&self, value: &mut Value) -> Result<(), Error> {
38        let text = match value {
39            Value::String(text) => text,
40            other => {
41                return Err(Error::new(ErrorKind::Type {
42                    expected: ValueType::String,
43                    found: other.type_name(),
44                }));
45            }
46        };
47
48        let parsed = match text.parse::<bytesize::ByteSize>() {
49            Ok(parsed) => parsed,
50            Err(_) => {
51                return Err(Error::new(ErrorKind::Format {
52                    expected: "byte size",
53                }));
54            }
55        };
56
57        let coerced = match isize::try_from(parsed.as_u64()) {
58            Ok(coerced) => coerced,
59            Err(_) => {
60                return Err(Error::new(ErrorKind::NotConvertible {
61                    target: ValueType::Int,
62                    found: ValueType::String,
63                }));
64            }
65        };
66
67        *value = Value::Int(coerced);
68        Ok(())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn coerces_to_bytes() {
78        let mut value = Value::String("10 KB".into());
79        ByteSize::new().validate(&mut value).unwrap();
80        assert_eq!(value, Value::Int(10_000));
81    }
82
83    #[test]
84    fn rejects_garbage() {
85        let mut value = Value::String("lots".into());
86        assert!(ByteSize::new().validate(&mut value).is_err());
87    }
88}