crazyflie_lib/
value.rs

1use half::f16;
2use std::convert::{TryFrom, TryInto};
3
4/// # Typed data value
5///
6/// This enum supports all the data types that can be exchanged with the Crazyflie
7/// using the [log](crate::subsystems::log) and [param](crate::subsystems::param) subsystems.
8///
9/// A function allows to convert a `[u8]` to a [Value] and the [Into<Vec<U8>>]
10/// trait is implemented to convert a [Value] into a vector of bytes.
11///
12/// The [TryFrom] trait is implemented for all matching rust primitive type. There
13/// is only direct conversion implemented. For example the following is OK:
14/// ```
15/// # use std::convert::TryInto;
16/// # use crazyflie_lib::Value;
17/// let v:u32 = Value::U32(42).try_into().unwrap();
18/// ```
19///
20/// However the following **will panic**:
21/// ``` should_panic
22/// # use std::convert::TryInto;
23/// # use crazyflie_lib::Value;
24/// let v:u32 = Value::U8(42).try_into().unwrap();
25/// ```
26#[derive(Debug, Clone, Copy)]
27pub enum Value {
28    /// [u8] value
29    U8(u8),
30    /// [u16] value
31    U16(u16),
32    /// [u32] value
33    U32(u32),
34    /// [u64] value
35    U64(u64),
36    /// [i8] value
37    I8(i8),
38    /// [i16] value
39    I16(i16),
40    /// [i32] value
41    I32(i32),
42    /// [i64] value
43    I64(i64),
44    /// [f16] value
45    F16(f16),
46    /// [f32] value
47    F32(f32),
48    /// [f64] value
49    F64(f64),
50}
51
52/// # Value type
53///
54/// This enum contains all the possible type of a [Value]
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum ValueType {
57    /// Type of a [u8] value
58    U8,
59    /// Type of a [u16] value
60    U16,
61    /// Type of a [u32] value
62    U32,
63    /// Type of a [u64] value
64    U64,
65    /// Type of a [i8] value
66    I8,
67    /// Type of a [i16] value
68    I16,
69    /// Type of a [i32] value
70    I32,
71    /// Type of a [i64] value
72    I64,
73    /// Type of a [f16] value
74    F16,
75    /// Type of a [f32] value
76    F32,
77    /// Type of a [f64] value
78    F64,
79}
80
81impl ValueType {
82    /// Return the byte length of a value according to its type
83    pub fn byte_length(&self) -> usize {
84        match self {
85            ValueType::U8 => 1,
86            ValueType::U16 => 2,
87            ValueType::U32 => 4,
88            ValueType::U64 => 8,
89            ValueType::I8 => 1,
90            ValueType::I16 => 2,
91            ValueType::I32 => 4,
92            ValueType::I64 => 8,
93            ValueType::F16 => 2,
94            ValueType::F32 => 4,
95            ValueType::F64 => 8,
96        }
97    }
98}
99
100impl From<Value> for ValueType {
101    fn from(value: Value) -> Self {
102        match value {
103            Value::U8(_) => ValueType::U8,
104            Value::U16(_) => ValueType::U16,
105            Value::U32(_) => ValueType::U32,
106            Value::U64(_) => ValueType::U64,
107            Value::I8(_) => ValueType::I8,
108            Value::I16(_) => ValueType::I16,
109            Value::I32(_) => ValueType::I32,
110            Value::I64(_) => ValueType::I64,
111            Value::F16(_) => ValueType::F16,
112            Value::F32(_) => ValueType::F32,
113            Value::F64(_) => ValueType::F64,
114        }
115    }
116}
117
118// Conversion from and to matching primitive types
119
120macro_rules! primitive_impl {
121    ($ty:ident, $name:ident) => {
122        impl From<$ty> for Value {
123            fn from(v: $ty) -> Self {
124                Value::$name(v)
125            }
126        }
127
128        impl TryFrom<Value> for $ty {
129            type Error = crate::Error;
130
131            fn try_from(value: Value) -> Result<$ty, Self::Error> {
132                match value {
133                    Value::$name(v) => Ok(v),
134                    _ => Err(Self::Error::ConversionError(format!(
135                        "Cannot convert {:?} to {}",
136                        value,
137                        stringify!($ty)
138                    ))),
139                }
140            }
141        }
142    };
143}
144
145primitive_impl!(u8, U8);
146primitive_impl!(u16, U16);
147primitive_impl!(u32, U32);
148primitive_impl!(u64, U64);
149primitive_impl!(i8, I8);
150primitive_impl!(i16, I16);
151primitive_impl!(i32, I32);
152primitive_impl!(i64, I64);
153primitive_impl!(f16, F16);
154primitive_impl!(f32, F32);
155primitive_impl!(f64, F64);
156
157// Conversion from and to u8 slice (little endian serde)
158impl Value {
159    /// Convert a `&[u8]` slice to a [Value]. The length of the slice must match
160    /// the length of the value type, otherwise an error will be returned.
161    pub fn from_le_bytes(bytes: &[u8], value_type: ValueType) -> Result<Value, crate::Error> {
162        match value_type {
163            ValueType::U8 => Ok(Value::U8(u8::from_le_bytes(bytes.try_into()?))),
164            ValueType::U16 => Ok(Value::U16(u16::from_le_bytes(bytes.try_into()?))),
165            ValueType::U32 => Ok(Value::U32(u32::from_le_bytes(bytes.try_into()?))),
166            ValueType::U64 => Ok(Value::U64(u64::from_le_bytes(bytes.try_into()?))),
167            ValueType::I8 => Ok(Value::I8(i8::from_le_bytes(bytes.try_into()?))),
168            ValueType::I16 => Ok(Value::I16(i16::from_le_bytes(bytes.try_into()?))),
169            ValueType::I32 => Ok(Value::I32(i32::from_le_bytes(bytes.try_into()?))),
170            ValueType::I64 => Ok(Value::I64(i64::from_le_bytes(bytes.try_into()?))),
171            ValueType::F16 => Ok(Value::F16(f16::from_le_bytes(bytes.try_into()?))),
172            ValueType::F32 => Ok(Value::F32(f32::from_le_bytes(bytes.try_into()?))),
173            ValueType::F64 => Ok(Value::F64(f64::from_le_bytes(bytes.try_into()?))),
174        }
175    }
176
177    /// Convert a [Value] to a [f64].
178    ///
179    /// This conversion is lossless in most case but can be lossy if the value
180    /// is a u64: a f64 cannot accurately store large values of a u64.
181    pub fn to_f64_lossy(&self) -> f64 {
182        match *self {
183            Value::U8(v) => v as f64,
184            Value::U16(v) => v as f64,
185            Value::U32(v) => v as f64,
186            Value::U64(v) => v as f64,
187            Value::I8(v) => v as f64,
188            Value::I16(v) => v as f64,
189            Value::I32(v) => v as f64,
190            Value::I64(v) => v as f64,
191            Value::F16(v) => v.to_f64(),
192            Value::F32(v) => v as f64,
193            Value::F64(v) => v as f64,
194        }
195    }
196
197    /// Make a [Value] from a [f64] and a [ValueType]
198    ///
199    /// This function allows to construct any type of value from a f64.
200    ///
201    /// The conversion has possibility to be lossy in a couple of cases:
202    ///  - When making an integer, the value is truncated to the number of bit of the parameter
203    ///    - Example: Setting `257` to a `u8` variable will set it to the value `1`
204    ///  - Similarly floating point precision will be truncated to the parameter precision. Rounding is undefined.
205    ///  - Making a floating point outside the range of the parameter is undefined.
206    ///  - It is not possible to represent accurately a `u64` parameter in a `f64`.
207    pub fn from_f64_lossy(value_type: ValueType, value: f64) -> Value {
208        match value_type {
209            ValueType::U8 => Value::U8((value as u64) as u8),
210            ValueType::U16 => Value::U16((value as u64) as u16),
211            ValueType::U32 => Value::U32((value as u64) as u32),
212            ValueType::U64 => Value::U64((value as u64) as u64),
213            ValueType::I8 => Value::I8((value as i64) as i8),
214            ValueType::I16 => Value::I16((value as i64) as i16),
215            ValueType::I32 => Value::I32((value as i64) as i32),
216            ValueType::I64 => Value::I64((value as i64) as i64),
217            ValueType::F16 => Value::F16(f16::from_f64(value)),
218            ValueType::F32 => Value::F32(value as f32),
219            ValueType::F64 => Value::F64(value),
220        }
221    }
222}
223
224impl From<Value> for Vec<u8> {
225    fn from(value: Value) -> Self {
226        match value {
227            Value::U8(v) => v.to_le_bytes().into(),
228            Value::U16(v) => v.to_le_bytes().into(),
229            Value::U32(v) => v.to_le_bytes().into(),
230            Value::U64(v) => v.to_le_bytes().into(),
231            Value::I8(v) => v.to_le_bytes().into(),
232            Value::I16(v) => v.to_le_bytes().into(),
233            Value::I32(v) => v.to_le_bytes().into(),
234            Value::I64(v) => v.to_le_bytes().into(),
235            Value::F16(v) => v.to_le_bytes().into(),
236            Value::F32(v) => v.to_le_bytes().into(),
237            Value::F64(v) => v.to_le_bytes().into(),
238        }
239    }
240}