mint_cli/layout/
conversions.rs

1use super::entry::ScalarType;
2use super::errors::LayoutError;
3use super::settings::{EndianBytes, Endianness};
4use super::value::DataValue;
5
6macro_rules! impl_try_from_data_value {
7    ($($t:ty),* $(,)?) => {$(
8        impl TryFrom<&DataValue> for $t {
9            type Error = LayoutError;
10            fn try_from(value: &DataValue) -> Result<Self, LayoutError> {
11                match value {
12                    DataValue::U64(val) => Ok(*val as $t),
13                    DataValue::I64(val) => Ok(*val as $t),
14                    DataValue::F64(val) => Ok(*val as $t),
15                    DataValue::Str(_) => {
16                        return Err(LayoutError::DataValueExportFailed(
17                            "Cannot convert string to scalar type.".to_string(),
18                        ));
19                    }
20                }
21            }
22        }
23    )* }; }
24
25impl_try_from_data_value!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
26
27pub trait TryFromStrict<T>: Sized {
28    fn try_from_strict(value: T) -> Result<Self, LayoutError>;
29}
30
31macro_rules! err {
32    ($msg:expr) => {
33        LayoutError::DataValueExportFailed($msg.to_string())
34    };
35}
36
37macro_rules! impl_try_from_strict_unsigned {
38    ($($t:ty),* $(,)?) => {$(
39        impl TryFromStrict<&DataValue> for $t {
40            fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
41                match value {
42                    DataValue::U64(v) => <Self as TryFrom<u64>>::try_from(*v)
43                        .map_err(|_| err!(format!("u64 value {} out of range for {}", v, stringify!($t)))),
44                    DataValue::I64(v) => {
45                        if *v < 0 { return Err(err!("negative integer cannot convert to unsigned in strict mode")); }
46                        <Self as TryFrom<u64>>::try_from(*v as u64)
47                            .map_err(|_| err!(format!("i64 value {} out of range for {}", v, stringify!($t))))
48                    }
49                    DataValue::F64(v) => {
50                        if !v.is_finite() { return Err(err!("non-finite float cannot convert to integer in strict mode")); }
51                        if v.fract() != 0.0 { return Err(err!("float to integer conversion not allowed unless value is an exact integer")); }
52                        if *v < 0.0 || *v > (<$t>::MAX as f64) { return Err(err!(format!("float value {} out of range for {}", v, stringify!($t)))); }
53                        Ok(*v as $t)
54                    }
55                    DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
56                }
57            }
58        }
59    )*};
60}
61
62macro_rules! impl_try_from_strict_signed {
63    ($($t:ty),* $(,)?) => {$(
64        impl TryFromStrict<&DataValue> for $t {
65            fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
66                match value {
67                    DataValue::U64(v) => {
68                        <Self as TryFrom<i128>>::try_from(*v as i128)
69                            .map_err(|_| err!(format!("u64 value {} out of range for {}", v, stringify!($t))))
70                    }
71                    DataValue::I64(v) => <Self as TryFrom<i64>>::try_from(*v)
72                        .map_err(|_| err!(format!("i64 value {} out of range for {}", v, stringify!($t)))),
73                    DataValue::F64(v) => {
74                        if !v.is_finite() { return Err(err!("non-finite float cannot convert to integer in strict mode")); }
75                        if v.fract() != 0.0 { return Err(err!("float to integer conversion not allowed unless value is an exact integer")); }
76                        if *v < (<$t>::MIN as f64) || *v > (<$t>::MAX as f64) { return Err(err!(format!("float value {} out of range for {}", v, stringify!($t)))); }
77                        Ok(*v as $t)
78                    }
79                    DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
80                }
81            }
82        }
83    )*};
84}
85
86macro_rules! impl_try_from_strict_float_targets {
87    ($t:ty) => {
88        impl TryFromStrict<&DataValue> for $t {
89            fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
90                match value {
91                    DataValue::F64(v) => {
92                        if !v.is_finite() {
93                            return Err(err!("non-finite float not allowed in strict mode"));
94                        }
95                        let out = *v as $t;
96                        if out.is_finite() {
97                            Ok(out)
98                        } else {
99                            Err(err!(format!(
100                                "float value {} out of range for {}",
101                                v,
102                                stringify!($t)
103                            )))
104                        }
105                    }
106                    DataValue::U64(v) => {
107                        let out = (*v as $t);
108                        if !out.is_finite() {
109                            return Err(err!("integer to float produced non-finite value"));
110                        }
111                        // exactness check via round-trip
112                        if (out as u64) == *v {
113                            Ok(out)
114                        } else {
115                            Err(err!(
116                                "lossy integer to float conversion not allowed in strict mode"
117                            ))
118                        }
119                    }
120                    DataValue::I64(v) => {
121                        let out = (*v as $t);
122                        if !out.is_finite() {
123                            return Err(err!("integer to float produced non-finite value"));
124                        }
125                        if (out as i64) == *v {
126                            Ok(out)
127                        } else {
128                            Err(err!(
129                                "lossy integer to float conversion not allowed in strict mode"
130                            ))
131                        }
132                    }
133                    DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
134                }
135            }
136        }
137    };
138}
139
140impl_try_from_strict_unsigned!(u8, u16, u32, u64);
141impl_try_from_strict_signed!(i8, i16, i32, i64);
142impl_try_from_strict_float_targets!(f32);
143impl TryFromStrict<&DataValue> for f64 {
144    fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
145        match value {
146            DataValue::F64(v) => Ok(*v),
147            DataValue::U64(v) => {
148                let out = *v as f64;
149                if (out as u64) == *v {
150                    Ok(out)
151                } else {
152                    Err(err!(
153                        "lossy integer to float conversion not allowed in strict mode"
154                    ))
155                }
156            }
157            DataValue::I64(v) => {
158                let out = *v as f64;
159                if (out as i64) == *v {
160                    Ok(out)
161                } else {
162                    Err(err!(
163                        "lossy integer to float conversion not allowed in strict mode"
164                    ))
165                }
166            }
167            DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
168        }
169    }
170}
171
172pub fn convert_value_to_bytes(
173    value: &DataValue,
174    scalar_type: ScalarType,
175    endianness: &Endianness,
176    strict: bool,
177) -> Result<Vec<u8>, LayoutError> {
178    macro_rules! to_bytes {
179        ($t:ty) => {{
180            let val: $t = if strict {
181                <$t as TryFromStrict<&DataValue>>::try_from_strict(value)?
182            } else {
183                <$t as TryFrom<&DataValue>>::try_from(value)?
184            };
185            Ok(val.to_endian_bytes(endianness))
186        }};
187    }
188
189    match scalar_type {
190        ScalarType::U8 => to_bytes!(u8),
191        ScalarType::I8 => to_bytes!(i8),
192        ScalarType::U16 => to_bytes!(u16),
193        ScalarType::I16 => to_bytes!(i16),
194        ScalarType::U32 => to_bytes!(u32),
195        ScalarType::I32 => to_bytes!(i32),
196        ScalarType::U64 => to_bytes!(u64),
197        ScalarType::I64 => to_bytes!(i64),
198        ScalarType::F32 => to_bytes!(f32),
199        ScalarType::F64 => to_bytes!(f64),
200    }
201}