mint_cli/layout/
conversions.rs1use 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 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}