lagan/
entry.rs

1use std::ffi::CString;
2
3use ntcore_sys::{
4    NT_Entry, NT_EntryFlags, NT_GetEntryType, NT_GetEntryValue, NT_Now, NT_Release, NT_SetEntryFlags, NT_SetEntryValue, NT_Value, NT_ValueData, NT_ValueDataArray, WPI_String
5};
6use snafu::ensure;
7
8use crate::{
9    nt_types::{RawValue, ValueFlags, ValueType}, Instance, NetworkTablesError, SetToUnassignedSnafu, UnassignedFlagsSnafu, Value
10};
11
12#[derive(Debug, PartialEq, Eq, Hash)]
13pub struct Entry<'a, I: Instance + ?Sized> {
14    pub(crate) instance: &'a I,
15    pub(crate) handle: NT_Entry,
16    pub(crate) name: String,
17}
18
19macro_rules! typed_value_getter {
20    {$($ident:ident: $variant:ident => $ty:ty),*} => {
21        $(
22            /// Returns the value of this entry if it is of the specified type.
23            /// Returns `None` if the type of the entry is not of the specified type.
24            pub fn $ident(&self) -> Option<$ty> {
25                match self.value() {
26                    Value::$variant(value) => Some(value),
27                    _ => None,
28                }
29            }
30        )*
31    };
32}
33macro_rules! typed_value_setter {
34    {$($ident:ident: $ty:ty => $variant:ident),*} => {
35        $(
36            /// Sets the value of this entry to the given value if it is of the type of the given value.
37            ///
38            /// # Errors
39            ///
40            /// - [`NetworkTablesEntryError::InvalidType`] if the type of the entry is not of the specified type.
41            pub fn $ident(&self, value: $ty) -> Result<(), NetworkTablesError> {
42                self.set_value(Value::$variant(value))
43            }
44        )*
45    };
46}
47
48impl<I: Instance + ?Sized> Entry<'_, I> {
49    pub fn value(&self) -> Value {
50        self.raw_value().data
51    }
52
53    typed_value_getter! {
54        value_bool: Bool => bool,
55        value_i64: I64 => i64,
56        value_f32: F32 => f32,
57        value_f64: F64 => f64,
58        value_string: String => String,
59        value_raw: Raw => Vec<u8>,
60        value_bool_array: BoolArray => Vec<bool>,
61        value_f64_array: F64Array => Vec<f64>,
62        value_f32_array: F32Array => Vec<f32>,
63        value_i64_array: I64Array => Vec<i64>,
64        value_string_array: StringArray => Vec<String>
65    }
66
67    pub fn value_type(&self) -> ValueType {
68        unsafe { NT_GetEntryType(self.handle()) }.into()
69    }
70
71    pub fn set_value(&self, value: Value) -> Result<(), NetworkTablesError> {
72        let current_value = self.raw_value();
73        let current_type = current_value.data.value_type();
74
75        if current_type != ValueType::Unassigned && current_type != value.value_type()
76        {
77            return Err(NetworkTablesError::InvalidType {
78                current_type,
79                given_type: value.value_type(),
80            });
81        }
82
83        let timestamp = unsafe { NT_Now() };
84        let mut new_value = NT_Value {
85            r#type: value.value_type().into(),
86            last_change: timestamp,
87            server_time: current_value.server_time.as_micros() as _,
88            data: unsafe { std::mem::zeroed() },
89        };
90        if self.instance.is_server() {
91            new_value.server_time = timestamp;
92        }
93
94        macro_rules! set_simple_array {
95            ($field:ident => $union:ident) => {{
96                new_value.data = NT_ValueData {
97                    $union: NT_ValueDataArray {
98                        arr: $field.as_ptr(),
99                        size: $field.len() as _,
100                    },
101                };
102                let status = unsafe { NT_SetEntryValue(self.handle(), &raw const new_value) };
103                debug_assert_eq!(status, 1);
104                return Ok(());
105            }};
106        }
107
108        //Safety: This raw data cannot be used after the values it points to are dropped.
109        //Safety: for this reason, the types that store pointers have to be used inside the match arms.
110        let raw_value_data = match value {
111            Value::Unassigned => return SetToUnassignedSnafu.fail(),
112            Value::Bool(value) => NT_ValueData {
113                v_boolean: value as _,
114            },
115            Value::I64(value) => NT_ValueData { v_int: value },
116            Value::F32(value) => NT_ValueData { v_float: value },
117            Value::F64(value) => NT_ValueData { v_double: value },
118            Value::String(string) => {
119                let string = CString::new(string).unwrap();
120                let wpi_string = WPI_String::from(string.as_c_str());
121                new_value.data = NT_ValueData {
122                    v_string: wpi_string,
123                };
124                let status = unsafe { NT_SetEntryValue(self.handle(), &raw const new_value) };
125                debug_assert_eq!(status, 1);
126                return Ok(());
127            }
128            Value::Raw(data) => set_simple_array!(data => v_raw),
129            Value::F64Array(array) => set_simple_array!(array => arr_double),
130            Value::F32Array(array) => set_simple_array!(array => arr_float),
131            Value::I64Array(array) => set_simple_array!(array => arr_int),
132            Value::BoolArray(array) => {
133                let bools = array.into_iter().map(|b| b.into()).collect::<Vec<_>>();
134                new_value.data = NT_ValueData {
135                    arr_boolean: NT_ValueDataArray {
136                        arr: bools.as_ptr(),
137                        size: bools.len() as _,
138                    },
139                };
140                let status = unsafe { NT_SetEntryValue(self.handle(), &raw const new_value) };
141                debug_assert_eq!(status, 1);
142                return Ok(());
143            }
144            Value::StringArray(array) => {
145                let c_strings = array
146                    .into_iter()
147                    .map(|s| CString::new(s).unwrap())
148                    .collect::<Vec<_>>();
149                let wpi_strings = c_strings
150                    .iter()
151                    .map(|s| WPI_String::from(s.as_c_str()))
152                    .collect::<Vec<_>>();
153                new_value.data = NT_ValueData {
154                    arr_string: NT_ValueDataArray {
155                        arr: wpi_strings.as_ptr(),
156                        size: wpi_strings.len() as _,
157                    },
158                };
159                let status = unsafe { NT_SetEntryValue(self.handle(), &raw const new_value) };
160                debug_assert_eq!(status, 1);
161                return Ok(());
162            }
163        };
164
165        new_value.data = raw_value_data;
166
167        let status = unsafe { NT_SetEntryValue(self.handle(), &raw const new_value) };
168        debug_assert_eq!(status, 1);
169
170        Ok(())
171    }
172
173    typed_value_setter! {
174        set_value_bool: bool => Bool,
175        set_value_i64: i64 => I64,
176        set_value_f32: f32 => F32,
177        set_value_f64: f64 => F64,
178        set_value_raw: Vec<u8> => Raw,
179        set_value_bool_array: Vec<bool> => BoolArray,
180        set_value_f64_array: Vec<f64> => F64Array,
181        set_value_f32_array: Vec<f32> => F32Array,
182        set_value_i64_array: Vec<i64> => I64Array,
183        set_value_string_array: Vec<String> => StringArray
184    }
185
186    /// Sets the value of this entry to the given value if it is of the type of the given value.
187    ///
188    /// # Errors
189    ///
190    /// - [`NetworkTablesEntryError::InvalidType`] if the type of the entry is not of the specified type.
191    pub fn set_value_string(&self, value: impl AsRef<str>) -> Result<(), NetworkTablesError> {
192        self.set_value(Value::String(value.as_ref().to_owned()))
193    }
194
195    pub fn set_flags(&self, flags: ValueFlags) -> Result<(), NetworkTablesError> {
196        ensure!(self.is_assigned(), UnassignedFlagsSnafu);
197        unsafe {
198            NT_SetEntryFlags(self.handle(), NT_EntryFlags::from_bits_retain(flags.bits()));
199        }
200        Ok(())
201    }
202
203    pub fn is_assigned(&self) -> bool {
204        !matches!(self.value_type(), ValueType::Unassigned)
205    }
206    pub fn is_unassigned(&self) -> bool {
207        !self.is_assigned()
208    }
209
210    pub fn name(&self) -> &str {
211        &self.name
212    }
213
214    pub fn raw_value(&self) -> RawValue {
215        let mut raw_value = unsafe { std::mem::zeroed() };
216        unsafe {
217            NT_GetEntryValue(self.handle(), &raw mut raw_value);
218        }
219        raw_value.into()
220    }
221
222    /// # Safety
223    ///
224    /// Caller must ensure that the returned handle is only used while the table entry is valid.
225    pub unsafe fn handle(&self) -> NT_Entry {
226        self.handle
227    }
228}
229
230impl<I: Instance + ?Sized> Drop for Entry<'_, I> {
231    fn drop(&mut self) {
232        unsafe {
233            NT_Release(self.handle());
234        }
235    }
236}