Skip to main content

frclib_core/value/
mod.rs

1//! This module contains the [``FrcValue``](crate::value::FrcValue) type which is used to represent values in various frc protocols.
2
3use std::{
4    fmt::Display,
5    hash::{Hash, Hasher},
6    io::Cursor,
7};
8
9mod error;
10#[cfg(test)]
11mod test;
12mod trait_impls;
13mod traits;
14
15use crate::structure::{FrcStructDesc, FrcStructure, FrcStructureBytes};
16pub use error::FrcValueCastError;
17pub use traits::IntoFrcValue;
18pub use traits::StaticallyFrcTyped;
19
20pub use inventory;
21
22use self::error::CastErrorReason;
23
24/// Measured in microseconds
25///
26/// depending on source can be from unix epoch or some arbitrary start time
27pub type FrcTimestamp = u64;
28
29#[allow(missing_docs)]
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub enum FrcType {
32    Void,
33    Raw,
34    Boolean,
35    Int,
36    Double,
37    Float,
38    String,
39    BooleanArray,
40    IntArray,
41    FloatArray,
42    DoubleArray,
43    StringArray,
44    Struct(&'static FrcStructDesc),
45    StructArray(&'static FrcStructDesc),
46}
47impl Display for FrcType {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            Self::Void => write!(f, "Void"),
51            Self::Boolean => write!(f, "Boolean"),
52            Self::Int => write!(f, "Int"),
53            Self::Double => write!(f, "Double"),
54            Self::Float => write!(f, "Float"),
55            Self::String => write!(f, "String"),
56            Self::BooleanArray => write!(f, "BooleanArray"),
57            Self::IntArray => write!(f, "IntArray"),
58            Self::FloatArray => write!(f, "FloatArray"),
59            Self::DoubleArray => write!(f, "DoubleArray"),
60            Self::StringArray => write!(f, "StringArray"),
61            Self::Raw => write!(f, "Raw"),
62            Self::Struct(desc) => write!(f, "Struct({})", desc.type_str),
63            Self::StructArray(desc) => write!(f, "StructArray({})", desc.type_str),
64        }
65    }
66}
67
68type BoxVec<T> = Box<[T]>;
69type BoxStr = Box<str>;
70
71/// A stardized value type for FRC data piping
72///
73/// This enum is used to represent all possible values that can be sent over the FRC data piping system
74/// including
75/// - ``Void``
76/// - ``Raw``
77/// - ``Boolean``
78/// - ``Int``
79/// - ``Double``
80/// - ``Float``
81/// - ``String``
82/// - ``BoolArray``
83/// - ``IntArray``
84/// - ``FloatArray``
85/// - ``DoubleArray``
86/// - ``StringArray``
87/// - ``Struct``
88/// - ``StructArray``
89#[allow(missing_docs)]
90#[derive(Debug, Clone, PartialEq)]
91pub enum FrcValue {
92    /// A value representing nothing, most api's that accept [`FrcValue`]
93    /// simply ignore this variant but it is important for full compatability
94    /// with things like json and messagepack
95    Void,
96    /// An immutable contigous chunk of bytes
97    Raw(BoxVec<u8>),
98    /// A single boolean
99    Boolean(bool),
100    /// A signed 64bit integer, this is the only integer available due to the limitations
101    /// of some parts of the wpilib ecosystem
102    Int(i64),
103    Double(f64),
104    Float(f32),
105    String(Box<str>),
106    BooleanArray(BoxVec<bool>),
107    IntArray(BoxVec<i64>),
108    FloatArray(BoxVec<f32>),
109    DoubleArray(BoxVec<f64>),
110    /// A contigous immutable list of heap allocated strings,
111    /// this variant uses the most heap allocations and should be avoided if possible
112    StringArray(BoxVec<BoxStr>),
113    /// A single struct represented by a contigous chunk of bytes and a schema.
114    /// The bytes are stored behind nested boxes to try and keep the enum size small
115    Struct(Box<FrcStructureBytes>),
116    /// A list of structs represented by a contigous chunk of bytes, a schema and count.
117    /// The bytes are stored behind nested boxes to try and keep the enum size small
118    StructArray(Box<FrcStructureBytes>),
119}
120impl Display for FrcValue {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        match self {
123            Self::Void => write!(f, "Void"),
124            Self::Boolean(v) => write!(f, "{v}"),
125            Self::Int(v) => write!(f, "{v}"),
126            Self::Double(v) => write!(f, "{v}"),
127            Self::Float(v) => write!(f, "{v}"),
128            Self::String(v) => write!(f, "{v}"),
129            Self::BooleanArray(v) => write!(f, "{v:?}"),
130            Self::IntArray(v) => write!(f, "{v:?}"),
131            Self::FloatArray(v) => write!(f, "{v:?}"),
132            Self::DoubleArray(v) => write!(f, "{v:?}"),
133            Self::StringArray(v) => write!(f, "{v:?}"),
134            Self::Raw(v) => write!(f, "{v:?}"),
135            Self::Struct(bytes) => write!(f, "Struct({}):{:?}", bytes.desc.type_str, bytes.data),
136            Self::StructArray(bytes) => write!(
137                f,
138                "Struct({})[{}]:{:?}",
139                bytes.desc.type_str, bytes.count, bytes.data
140            ),
141        }
142    }
143}
144impl Hash for FrcValue {
145    fn hash<H: Hasher>(&self, state: &mut H) {
146        match self {
147            Self::Void => {}
148            Self::Boolean(v) => v.hash(state),
149            Self::Int(v) => v.hash(state),
150            Self::Double(v) => v.to_bits().hash(state),
151            Self::Float(v) => v.to_bits().hash(state),
152            Self::String(v) => v.hash(state),
153            Self::BooleanArray(v) => v.hash(state),
154            Self::IntArray(v) => v.hash(state),
155            Self::FloatArray(v) => v.iter().for_each(|v| v.to_bits().hash(state)),
156            Self::DoubleArray(v) => v.iter().for_each(|v| v.to_bits().hash(state)),
157            Self::StringArray(v) => v.hash(state),
158            Self::Raw(v) => v.hash(state),
159            Self::Struct(bytes) | Self::StructArray(bytes) => {
160                bytes.desc.schema_supplier.hash(state);
161                bytes.desc.type_str.hash(state);
162                bytes.data.hash(state);
163            }
164        }
165    }
166}
167impl FrcValue {
168    ///Returns the type enum of the value, a more memory efficient way of checking the type
169    #[must_use]
170    pub const fn get_type(&self) -> FrcType {
171        match self {
172            Self::Void => FrcType::Void,
173            Self::Boolean(_) => FrcType::Boolean,
174            Self::Int(_) => FrcType::Int,
175            Self::Double(_) => FrcType::Double,
176            Self::Float(_) => FrcType::Float,
177            Self::String(_) => FrcType::String,
178            Self::BooleanArray(_) => FrcType::BooleanArray,
179            Self::IntArray(_) => FrcType::IntArray,
180            Self::FloatArray(_) => FrcType::FloatArray,
181            Self::DoubleArray(_) => FrcType::DoubleArray,
182            Self::StringArray(_) => FrcType::StringArray,
183            Self::Raw(_) => FrcType::Raw,
184            Self::Struct(s) => FrcType::Struct(s.desc),
185            Self::StructArray(s) => FrcType::StructArray(s.desc),
186        }
187    }
188    ///Creates an empty Binary
189    #[must_use]
190    pub const fn empty() -> Self {
191        Self::Void
192    }
193    /// Always false if not a collection or binary
194    #[must_use]
195    pub const fn is_empty(&self) -> bool {
196        match self {
197            Self::Void => true,
198            Self::String(v) => v.is_empty(),
199            Self::BooleanArray(v) => v.is_empty(),
200            Self::IntArray(v) => v.is_empty(),
201            Self::DoubleArray(v) => v.is_empty(),
202            Self::FloatArray(v) => v.is_empty(),
203            Self::StringArray(v) => v.is_empty(),
204            Self::Raw(v) => v.is_empty(),
205            Self::Struct(bytes) | Self::StructArray(bytes) => bytes.data.is_empty(),
206            _ => false,
207        }
208    }
209    ///Binary is false
210    #[must_use]
211    pub const fn is_array(&self) -> bool {
212        matches!(
213            self,
214            Self::BooleanArray(_)
215                | Self::IntArray(_)
216                | Self::DoubleArray(_)
217                | Self::FloatArray(_)
218                | Self::StringArray(_)
219                | Self::StructArray(_)
220        )
221    }
222    /// Consumes itself to a timestamped value with the given timestamp
223    #[must_use]
224    pub const fn to_timestamped(self, timestamp: FrcTimestamp) -> FrcTimestampedValue {
225        FrcTimestampedValue::new(timestamp, self)
226    }
227    /// Creates a default value based on the type
228    ///
229    /// Types that will return none:
230    ///     - `Void`
231    ///     - `Struct`
232    ///     - `StructArray`
233    #[must_use]
234    pub fn default_value(r#type: FrcType) -> Option<Self> {
235        match r#type {
236            FrcType::Boolean => Some(Self::Boolean(false)),
237            FrcType::Int => Some(Self::Int(0)),
238            FrcType::Double => Some(Self::Double(0.0)),
239            FrcType::Float => Some(Self::Float(0.0)),
240            FrcType::String => Some(Self::String(Box::default())),
241            FrcType::BooleanArray => Some(Self::BooleanArray(Box::default())),
242            FrcType::IntArray => Some(Self::IntArray(Box::default())),
243            FrcType::FloatArray => Some(Self::FloatArray(Box::default())),
244            FrcType::DoubleArray => Some(Self::DoubleArray(Box::default())),
245            FrcType::StringArray => Some(Self::StringArray(Box::default())),
246            FrcType::Raw => Some(Self::Raw(Box::default())),
247            _ => None,
248        }
249    }
250}
251
252impl FrcValue {
253    /// Converts the given [``FrcStructure``](crate::structure::FrcStructure) into a [``FrcValue``](FrcValue)
254    pub fn from_struct<T: FrcStructure>(value: &T) -> Self {
255        let mut buffer = Vec::with_capacity(T::SIZE);
256        value.pack(&mut buffer);
257        Self::Struct(Box::new(FrcStructureBytes::from_parts(
258            &T::DESCRIPTION,
259            1,
260            buffer.into_boxed_slice(),
261        )))
262    }
263
264    /// # Errors
265    /// Returns an error if the value is not a struct or the struct is not the correct type
266    pub fn try_into_struct<T: FrcStructure>(self) -> Result<T, FrcValueCastError> {
267        let frc_type = self.get_type();
268        match self {
269            Self::Struct(bytes) => {
270                let buffer = bytes.data;
271                if buffer.len() == T::SIZE {
272                    let mut cursor = Cursor::new(buffer.as_ref());
273                    Ok(T::unpack(&mut cursor))
274                } else {
275                    Err(FrcValueCastError::InvalidCastTo(
276                        frc_type,
277                        T::TYPE,
278                        CastErrorReason::Deserialization,
279                    ))
280                }
281            }
282            _ => Err(FrcValueCastError::InvalidCastTo(
283                frc_type,
284                T::TYPE,
285                CastErrorReason::Type,
286            )),
287        }
288    }
289
290    /// Converts the given [``FrcStructure``](crate::structure::FrcStructure) slice/array into a [``FrcValue``](FrcValue)
291    pub fn from_struct_array<T: FrcStructure>(values: &[T]) -> Self {
292        let mut buffer = Vec::with_capacity(T::SIZE * values.len());
293        for value in values {
294            value.pack(&mut buffer);
295        }
296        Self::StructArray(Box::new(FrcStructureBytes::from_parts(
297            &T::DESCRIPTION,
298            values.len(),
299            buffer.into_boxed_slice(),
300        )))
301    }
302
303    /// # Errors
304    /// Returns an error if the value is not a struct or the struct is not the correct type
305    pub fn try_into_struct_array<T: FrcStructure>(self) -> Result<Vec<T>, FrcValueCastError> {
306        let frc_type = self.get_type();
307        match self {
308            Self::StructArray(bytes) => {
309                let buffer = bytes.data;
310                if buffer.len() % T::SIZE == 0 {
311                    let mut cursor = Cursor::new(buffer.as_ref());
312                    let mut values = Vec::with_capacity(buffer.len() / T::SIZE);
313                    while cursor.position() < buffer.len() as u64 {
314                        values.push(T::unpack(&mut cursor));
315                    }
316                    Ok(values)
317                } else {
318                    Err(FrcValueCastError::InvalidCastTo(
319                        frc_type,
320                        T::TYPE,
321                        CastErrorReason::Deserialization,
322                    ))
323                }
324            }
325            _ => Err(FrcValueCastError::InvalidCastTo(
326                frc_type,
327                T::TYPE,
328                CastErrorReason::Type,
329            )),
330        }
331    }
332}
333
334/// An [``FrcValue``](FrcValue) with a [``FrcTimestamp``](FrcTimestamp) attached,
335/// important for passing to logging systems
336#[derive(Debug, Clone, PartialEq, Hash)]
337pub struct FrcTimestampedValue {
338    /// The timestamp of the value,
339    /// typically the uptime of the robot if running on the robot
340    /// and the unix epoch if running on desktop (non sim)
341    pub timestamp: FrcTimestamp,
342    /// The value
343    pub value: FrcValue,
344}
345impl Display for FrcTimestampedValue {
346    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347        write!(f, "{} at {}", self.value, self.timestamp)
348    }
349}
350impl FrcTimestampedValue {
351    /// Creates a new timestamped value
352    #[must_use]
353    pub const fn new(timestamp: FrcTimestamp, value: FrcValue) -> Self {
354        Self { timestamp, value }
355    }
356    /// Checks if the timestamp is after the given timestamp
357    #[must_use]
358    pub const fn is_after_timestamp(&self, timestamp: FrcTimestamp) -> bool {
359        self.timestamp > timestamp
360    }
361    /// Checks if the timestamp is after the given timestamped value
362    #[must_use]
363    pub const fn is_after_other(&self, other: &Self) -> bool {
364        self.timestamp > other.timestamp
365    }
366    /// Checks if the timestamp is before the given timestamp
367    #[must_use]
368    pub const fn is_before_timestamp(&self, timestamp: FrcTimestamp) -> bool {
369        self.timestamp < timestamp
370    }
371    /// Checks if the timestamp is before the given timestamped value
372    #[must_use]
373    pub const fn is_before_other(&self, other: &Self) -> bool {
374        self.timestamp < other.timestamp
375    }
376}
377
378/// A timestamped value with a key attached,
379/// important for passing to pub/sub logging systems
380#[derive(Debug, Clone)]
381pub struct FrcEntry {
382    /// The timestamp of the value,
383    /// typically the uptime of the robot if running on the robot
384    /// and the unix epoch if running on desktop
385    pub timestamp: FrcTimestamp,
386    /// The value
387    pub value: FrcValue,
388    /// The key
389    pub key: &'static str,
390}
391impl Display for FrcEntry {
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        write!(f, "{} at {} for {}", self.value, self.timestamp, self.key)
394    }
395}