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!("float value {} out of range for {}", v, stringify!($t))))
100                        }
101                    }
102                    DataValue::U64(v) => {
103                        let out = (*v as $t);
104                        if !out.is_finite() {
105                            return Err(err!("integer to float produced non-finite value"));
106                        }
107                        // exactness check via round-trip
108                        if (out as u64) == *v {
109                            Ok(out)
110                        } else {
111                            Err(err!(
112                                "lossy integer to float conversion not allowed in strict mode"
113                            ))
114                        }
115                    }
116                    DataValue::I64(v) => {
117                        let out = (*v as $t);
118                        if !out.is_finite() {
119                            return Err(err!("integer to float produced non-finite value"));
120                        }
121                        if (out as i64) == *v {
122                            Ok(out)
123                        } else {
124                            Err(err!(
125                                "lossy integer to float conversion not allowed in strict mode"
126                            ))
127                        }
128                    }
129                    DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
130                }
131            }
132        }
133    };
134}
135
136impl_try_from_strict_unsigned!(u8, u16, u32, u64);
137impl_try_from_strict_signed!(i8, i16, i32, i64);
138impl_try_from_strict_float_targets!(f32);
139impl TryFromStrict<&DataValue> for f64 {
140    fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
141        match value {
142            DataValue::F64(v) => Ok(*v),
143            DataValue::U64(v) => {
144                let out = *v as f64;
145                if (out as u64) == *v {
146                    Ok(out)
147                } else {
148                    Err(err!(
149                        "lossy integer to float conversion not allowed in strict mode"
150                    ))
151                }
152            }
153            DataValue::I64(v) => {
154                let out = *v as f64;
155                if (out as i64) == *v {
156                    Ok(out)
157                } else {
158                    Err(err!(
159                        "lossy integer to float conversion not allowed in strict mode"
160                    ))
161                }
162            }
163            DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
164        }
165    }
166}
167
168pub fn convert_value_to_bytes(
169    value: &DataValue,
170    scalar_type: ScalarType,
171    endianness: &Endianness,
172    strict: bool,
173) -> Result<Vec<u8>, LayoutError> {
174    macro_rules! to_bytes {
175        ($t:ty) => {{
176            let val: $t = if strict {
177                <$t as TryFromStrict<&DataValue>>::try_from_strict(value)?
178            } else {
179                <$t as TryFrom<&DataValue>>::try_from(value)?
180            };
181            Ok(val.to_endian_bytes(endianness))
182        }};
183    }
184
185    match scalar_type {
186        ScalarType::U8 => to_bytes!(u8),
187        ScalarType::I8 => to_bytes!(i8),
188        ScalarType::U16 => to_bytes!(u16),
189        ScalarType::I16 => to_bytes!(i16),
190        ScalarType::U32 => to_bytes!(u32),
191        ScalarType::I32 => to_bytes!(i32),
192        ScalarType::U64 => to_bytes!(u64),
193        ScalarType::I64 => to_bytes!(i64),
194        ScalarType::F32 => to_bytes!(f32),
195        ScalarType::F64 => to_bytes!(f64),
196    }
197}