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!(
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 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}