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