1use crate::{
7 ParameterError,
8 types::{
9 FloatingPointRange, IntegerRange, MAX_PARAM_NAME_LEN, NodeFlags, NodeKey, Parameter,
10 ParameterDescriptor, ParameterRange, ParameterType, ParameterValue,
11 },
12};
13use heapless::String;
14
15use crate::types::MAX_PARAMETERS;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum ParameterEventKind {
23 New,
25 Changed,
27 Deleted,
29 Announce,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct PendingParameterEvent {
39 pub node: NodeKey,
41 pub kind: ParameterEventKind,
43 pub name: String<MAX_PARAM_NAME_LEN>,
45}
46
47pub const PARAMETER_EVENT_LOG_LEN: usize = 8;
56
57#[derive(Debug, Clone)]
63pub(crate) struct ParameterEntry {
64 node: NodeKey,
68 param: Parameter,
70 descriptor: Option<ParameterDescriptor>,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum DeclarationError {
88 AlreadyDeclared,
90 InvalidRange,
93 InitialValueOutOfRange,
96 StorageFull,
99 NameTooLong,
102 NoValueAvailable,
106}
107
108impl From<DeclarationError> for crate::ParameterError {
113 fn from(e: DeclarationError) -> Self {
114 match e {
115 DeclarationError::AlreadyDeclared => crate::ParameterError::TypeMismatch,
116 DeclarationError::InvalidRange => crate::ParameterError::InvalidRange,
117 DeclarationError::InitialValueOutOfRange => crate::ParameterError::OutOfRange,
118 DeclarationError::StorageFull | DeclarationError::NameTooLong => {
119 crate::ParameterError::StorageFull
120 }
121 DeclarationError::NoValueAvailable => crate::ParameterError::NotFound,
122 }
123 }
124}
125
126pub type OnSetParameterFn = fn(
144 node: NodeKey,
145 name: &str,
146 old: Option<&ParameterValue>,
147 new: Option<&ParameterValue>,
148 ctx: OnSetContext,
149) -> bool;
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
165pub struct OnSetContext {
166 pub a: usize,
169 pub b: usize,
171}
172
173impl OnSetContext {
174 pub const fn new(a: usize) -> Self {
176 Self { a, b: 0 }
177 }
178
179 pub const fn pair(a: usize, b: usize) -> Self {
181 Self { a, b }
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub struct OnSetParameterHandle(u16);
194
195impl OnSetParameterHandle {
196 pub const fn raw(self) -> u16 {
203 self.0
204 }
205
206 pub const fn from_raw(raw: u16) -> Self {
208 Self(raw)
209 }
210}
211
212#[derive(Clone, Copy)]
213struct OnSetCallbackSlot {
214 node: NodeKey,
215 callback: OnSetParameterFn,
216 ctx: OnSetContext,
217}
218
219pub const MAX_ON_SET_CALLBACKS: usize = 4;
235
236pub struct ParameterTable<'s> {
252 entries: &'s mut [Option<ParameterEntry>],
253}
254
255impl<'s> ParameterTable<'s> {
256 pub(crate) fn new(entries: &'s mut [Option<ParameterEntry>]) -> Self {
258 Self { entries }
259 }
260
261 pub fn capacity(&self) -> usize {
263 self.entries.len()
264 }
265
266 pub fn occupied(&self) -> usize {
268 self.entries.iter().filter(|slot| slot.is_some()).count()
269 }
270}
271
272#[repr(transparent)]
284pub struct ParameterSlot(Option<ParameterEntry>);
285
286impl<'s> ParameterTable<'s> {
287 pub fn init_in(slots: &'s mut [core::mem::MaybeUninit<ParameterSlot>]) -> Self {
295 for slot in slots.iter_mut() {
296 slot.write(ParameterSlot(None));
297 }
298 let len = slots.len();
299 let base = slots.as_mut_ptr().cast::<Option<ParameterEntry>>();
300 let entries = unsafe { core::slice::from_raw_parts_mut(base, len) };
305 Self::new(entries)
306 }
307}
308
309impl core::fmt::Debug for ParameterTable<'_> {
310 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
311 f.debug_struct("ParameterTable")
312 .field("capacity", &self.capacity())
313 .finish_non_exhaustive()
314 }
315}
316
317pub struct ParameterStorage<const N: usize = MAX_PARAMETERS> {
371 entries: [Option<ParameterEntry>; N],
372}
373
374impl<const N: usize> ParameterStorage<N> {
375 #[allow(clippy::new_without_default)]
384 #[allow(clippy::large_stack_arrays)] pub const fn new() -> Self {
386 Self {
387 entries: [const { None }; N],
388 }
389 }
390
391 pub const fn capacity(&self) -> usize {
393 N
394 }
395
396 pub fn as_table(&mut self) -> ParameterTable<'_> {
398 ParameterTable::new(&mut self.entries)
399 }
400
401 pub unsafe fn init_in_place(dst: *mut Self) {
418 unsafe {
422 let entries = core::ptr::addr_of_mut!((*dst).entries).cast::<Option<ParameterEntry>>();
423 for i in 0..N {
424 entries.add(i).write(None);
425 }
426 }
427 }
428}
429
430pub struct ParameterServer<'s> {
474 table: ParameterTable<'s>,
476 count: usize,
478 allow_undeclared: NodeFlags,
486 delete_on_not_set: NodeFlags,
492 truncation_pending: bool,
497 events_enabled: NodeFlags,
499 event_log: heapless::Deque<PendingParameterEvent, PARAMETER_EVENT_LOG_LEN>,
501 events_dropped: u32,
503 events_initial: NodeFlags,
510 initial_cursor: usize,
513 on_set: [Option<OnSetCallbackSlot>; MAX_ON_SET_CALLBACKS],
516}
517
518impl<'s> ParameterServer<'s> {
519 pub(crate) fn take_truncated_descriptions(
529 &mut self,
530 mut report: impl FnMut(NodeKey, &str),
531 ) -> usize {
532 if !self.truncation_pending {
533 return 0;
534 }
535 self.truncation_pending = false;
536 let mut n = 0;
537 for entry in self.table.entries.iter_mut().flatten() {
538 if let Some(d) = entry.descriptor.as_mut()
539 && d.description_truncated()
540 {
541 d.clear_description_truncated();
542 report(entry.node, entry.param.name.as_str());
543 n += 1;
544 }
545 }
546 n
547 }
548
549 #[inline]
551 fn note_descriptor(&mut self, descriptor: Option<&ParameterDescriptor>) {
552 if descriptor.is_some_and(ParameterDescriptor::description_truncated) {
553 self.truncation_pending = true;
554 }
555 }
556
557 pub(crate) fn new_in(table: ParameterTable<'s>) -> Self {
568 let count = table.occupied();
569 Self {
570 table,
571 count,
572 allow_undeclared: NodeFlags::new(),
573 delete_on_not_set: NodeFlags::new(),
574 on_set: [None; MAX_ON_SET_CALLBACKS],
575 truncation_pending: true,
578 events_enabled: NodeFlags::new(),
579 event_log: heapless::Deque::new(),
580 events_dropped: 0,
581 events_initial: NodeFlags::new(),
582 initial_cursor: 0,
583 }
584 }
585
586 pub(crate) fn set_allow_undeclared(&mut self, node: NodeKey, allow: bool) {
595 self.allow_undeclared.set(node, allow);
596 }
597
598 pub(crate) fn allows_undeclared(&self, node: NodeKey) -> bool {
600 self.allow_undeclared.get(node)
601 }
602
603 pub(crate) fn set_parameter_events(&mut self, node: NodeKey, on: bool) {
614 if on && !self.events_enabled.get(node) {
615 self.events_initial.set(node, true);
616 }
617 if !on {
618 self.events_initial.set(node, false);
619 if self.events_initial.first().is_none() {
620 self.initial_cursor = 0;
621 }
622 }
623 self.events_enabled.set(node, on);
624 }
625
626 pub(crate) fn parameter_events_enabled(&self, node: NodeKey) -> bool {
628 self.events_enabled.get(node)
629 }
630
631 pub(crate) fn pop_initial_parameter_event(&mut self) -> Option<PendingParameterEvent> {
641 loop {
642 let node = self.events_initial.first()?;
643 let k = self.initial_cursor;
644 let next = self.iter(node).nth(k).map(|p| p.name.clone());
645 match next {
646 Some(name) => {
647 self.initial_cursor = k + 1;
648 return Some(PendingParameterEvent {
649 node,
650 kind: ParameterEventKind::New,
651 name,
652 });
653 }
654 None => {
655 self.events_initial.set(node, false);
656 self.initial_cursor = 0;
657 if k == 0 {
658 return Some(PendingParameterEvent {
659 node,
660 kind: ParameterEventKind::Announce,
661 name: String::new(),
662 });
663 }
664 }
665 }
666 }
667 }
668
669 pub(crate) fn pop_parameter_event(&mut self) -> Option<PendingParameterEvent> {
671 self.event_log.pop_front()
672 }
673
674 pub(crate) fn take_parameter_events_dropped(&mut self) -> u32 {
677 core::mem::take(&mut self.events_dropped)
678 }
679
680 fn note_event(&mut self, node: NodeKey, kind: ParameterEventKind, name: &str) {
683 if !self.events_enabled.get(node) || self.events_initial.get(node) {
686 return;
687 }
688 if let Some(e) = self
689 .event_log
690 .iter_mut()
691 .find(|e| e.node == node && e.name.as_str() == name)
692 {
693 e.kind = match (e.kind, kind) {
696 (ParameterEventKind::New, ParameterEventKind::Changed) => ParameterEventKind::New,
697 (_, k) => k,
698 };
699 return;
700 }
701 let mut stored = String::new();
702 if stored.push_str(name).is_err()
703 || self
704 .event_log
705 .push_back(PendingParameterEvent {
706 node,
707 kind,
708 name: stored,
709 })
710 .is_err()
711 {
712 self.events_dropped = self.events_dropped.saturating_add(1);
713 }
714 }
715
716 pub(crate) fn set_delete_on_not_set(&mut self, node: NodeKey, on: bool) {
721 self.delete_on_not_set.set(node, on);
722 }
723
724 pub(crate) fn len(&self, node: NodeKey) -> usize {
726 self.entries_of(node).count()
727 }
728
729 pub(crate) fn is_empty(&self, node: NodeKey) -> bool {
731 self.len(node) == 0
732 }
733
734 pub(crate) fn total_len(&self) -> usize {
738 self.count
739 }
740
741 pub(crate) fn capacity(&self) -> usize {
749 self.table.capacity()
750 }
751
752 pub(crate) fn is_full(&self) -> bool {
754 self.count >= self.table.capacity()
755 }
756
757 fn entries_of(&self, node: NodeKey) -> impl Iterator<Item = &ParameterEntry> {
759 self.table
760 .entries
761 .iter()
762 .filter_map(|slot| slot.as_ref())
763 .filter(move |e| e.node == node)
764 }
765
766 fn find_index(&self, node: NodeKey, name: &str) -> Option<usize> {
771 self.table.entries.iter().position(|entry| {
772 entry
773 .as_ref()
774 .map(|e| e.node == node && e.param.name.as_str() == name)
775 .unwrap_or(false)
776 })
777 }
778
779 fn find_empty_slot(&self) -> Option<usize> {
781 self.table.entries.iter().position(|entry| entry.is_none())
782 }
783
784 #[must_use]
793 pub(crate) fn declare(&mut self, node: NodeKey, name: &str, value: ParameterValue) -> bool {
794 self.declare_with_descriptor(node, name, value, None)
795 }
796
797 #[must_use]
801 pub(crate) fn declare_ref(
802 &mut self,
803 node: NodeKey,
804 name: &str,
805 value: &ParameterValue,
806 ) -> bool {
807 self.try_declare_ref(node, name, value, None).is_ok()
808 }
809
810 #[must_use]
817 pub(crate) fn declare_with_descriptor(
818 &mut self,
819 node: NodeKey,
820 name: &str,
821 value: ParameterValue,
822 descriptor: Option<ParameterDescriptor>,
823 ) -> bool {
824 self.try_declare_with_descriptor(node, name, value, descriptor)
825 .is_ok()
826 }
827
828 pub(crate) fn try_declare(
837 &mut self,
838 node: NodeKey,
839 name: &str,
840 value: ParameterValue,
841 ) -> Result<(), DeclarationError> {
842 self.try_declare_with_descriptor(node, name, value, None)
843 }
844
845 pub(crate) fn try_declare_with_descriptor(
848 &mut self,
849 node: NodeKey,
850 name: &str,
851 value: ParameterValue,
852 descriptor: Option<ParameterDescriptor>,
853 ) -> Result<(), DeclarationError> {
854 self.try_declare_ref(node, name, &value, descriptor)
855 }
856
857 pub(crate) fn try_declare_ref(
870 &mut self,
871 node: NodeKey,
872 name: &str,
873 value: &ParameterValue,
874 descriptor: Option<ParameterDescriptor>,
875 ) -> Result<(), DeclarationError> {
876 if self.find_index(node, name).is_some() {
878 return Err(DeclarationError::AlreadyDeclared);
879 }
880
881 Self::declaration_consistency(descriptor.as_ref(), value)?;
884
885 let slot = match self.find_empty_slot() {
887 Some(idx) => idx,
888 None => return Err(DeclarationError::StorageFull),
889 };
890
891 let mut stored_name = String::new();
894 if stored_name.push_str(name).is_err() {
895 return Err(DeclarationError::NameTooLong);
896 }
897
898 self.note_descriptor(descriptor.as_ref());
899 let cell = &mut self.table.entries[slot];
903 *cell = Some(ParameterEntry {
904 node,
905 param: Parameter {
906 name: stored_name,
907 value: ParameterValue::NotSet,
908 },
909 descriptor,
910 });
911 if let Some(entry) = cell {
912 entry.param.value.assign_from(value);
913 }
914 self.count += 1;
915 self.note_event(node, ParameterEventKind::New, name);
916 Ok(())
917 }
918
919 pub(crate) fn declaration_consistency(
929 descriptor: Option<&ParameterDescriptor>,
930 value: &ParameterValue,
931 ) -> Result<(), DeclarationError> {
932 match descriptor {
933 Some(desc) if !desc.range.is_valid() => Err(DeclarationError::InvalidRange),
934 Some(desc) if !desc.validate_range(value) => {
935 Err(DeclarationError::InitialValueOutOfRange)
936 }
937 _ => Ok(()),
938 }
939 }
940
941 fn declaration_is_consistent(
942 descriptor: Option<&ParameterDescriptor>,
943 value: &ParameterValue,
944 ) -> bool {
945 Self::declaration_consistency(descriptor, value).is_ok()
946 }
947
948 fn check_set_existing(
951 entry: &ParameterEntry,
952 value: &ParameterValue,
953 ) -> Result<(), ParameterError> {
954 if let Some(ref desc) = entry.descriptor {
955 if desc.read_only {
956 return Err(ParameterError::ReadOnly);
957 }
958 if !desc.dynamic_typing && desc.param_type != value.param_type() {
959 return Err(ParameterError::TypeMismatch);
960 }
961 if !desc.validate_range(value) {
962 return Err(ParameterError::OutOfRange);
963 }
964 }
965 Ok(())
966 }
967
968 pub(crate) fn check_apply(
972 &self,
973 node: NodeKey,
974 name: &str,
975 value: &ParameterValue,
976 ) -> Result<(), ParameterError> {
977 match self
978 .find_index(node, name)
979 .and_then(|idx| self.table.entries[idx].as_ref())
980 {
981 Some(entry) => Self::check_set_existing(entry, value),
982 None if !self.allows_undeclared(node) => Err(ParameterError::Undeclared),
983 None if self.is_full() => Err(ParameterError::StorageFull),
984 None if name.len() > MAX_PARAM_NAME_LEN => Err(ParameterError::StorageFull),
985 None => Ok(()),
986 }
987 }
988
989 pub(crate) fn apply(
1000 &mut self,
1001 node: NodeKey,
1002 name: &str,
1003 value: ParameterValue,
1004 ) -> Result<(), ParameterError> {
1005 self.apply_ref(node, name, &value)
1006 }
1007
1008 pub(crate) fn apply_ref(
1014 &mut self,
1015 node: NodeKey,
1016 name: &str,
1017 value: &ParameterValue,
1018 ) -> Result<(), ParameterError> {
1019 if matches!(value, ParameterValue::NotSet) && self.delete_on_not_set.get(node) {
1020 return self.apply_delete(node, name);
1021 }
1022 self.check_apply(node, name, value)?;
1023 if !self.run_on_set_callbacks(node, name, self.get(node, name), Some(value)) {
1031 return Err(ParameterError::Rejected);
1032 }
1033 if self.find_index(node, name).is_some() {
1034 self.set_ref(node, name, value)
1035 } else if self.declare_ref(node, name, value) {
1036 Ok(())
1037 } else {
1038 Err(ParameterError::StorageFull)
1039 }
1040 }
1041
1042 pub(crate) fn apply_staged(
1059 &mut self,
1060 node: NodeKey,
1061 name: &str,
1062 staged: &mut ParameterValue,
1063 ) -> Result<(), ParameterError> {
1064 if matches!(staged, ParameterValue::NotSet) && self.delete_on_not_set.get(node) {
1065 return self.apply_delete(node, name);
1066 }
1067 self.check_apply(node, name, staged)?;
1068 if !self.run_on_set_callbacks(node, name, self.get(node, name), Some(staged)) {
1069 return Err(ParameterError::Rejected);
1070 }
1071 let (idx, kind) = match self.find_index(node, name) {
1072 Some(idx) => (idx, ParameterEventKind::Changed),
1073 None => match self.declare_placeholder(node, name) {
1074 Some(idx) => (idx, ParameterEventKind::New),
1075 None => return Err(ParameterError::StorageFull),
1076 },
1077 };
1078 match self.table.entries[idx].as_mut() {
1079 Some(entry) => {
1080 core::mem::swap(&mut entry.param.value, staged);
1081 self.note_event(node, kind, name);
1082 Ok(())
1083 }
1084 None => Err(ParameterError::NotFound),
1085 }
1086 }
1087
1088 #[inline(never)]
1095 fn declare_placeholder(&mut self, node: NodeKey, name: &str) -> Option<usize> {
1096 let slot = self.find_empty_slot()?;
1097 let mut stored = String::new();
1098 stored.push_str(name).ok()?;
1099 self.table.entries[slot] = Some(ParameterEntry {
1100 node,
1101 param: Parameter {
1102 name: stored,
1103 value: ParameterValue::NotSet,
1104 },
1105 descriptor: None,
1106 });
1107 self.count += 1;
1108 Some(slot)
1109 }
1110
1111 fn apply_delete(&mut self, node: NodeKey, name: &str) -> Result<(), ParameterError> {
1115 let Some(entry) = self
1116 .find_index(node, name)
1117 .and_then(|idx| self.table.entries[idx].as_ref())
1118 else {
1119 return Err(ParameterError::NotFound);
1120 };
1121 if entry.descriptor.as_ref().is_some_and(|d| d.read_only) {
1122 return Err(ParameterError::ReadOnly);
1123 }
1124 if !self.run_on_set_callbacks(node, name, self.get(node, name), None) {
1125 return Err(ParameterError::Rejected);
1126 }
1127 self.remove(node, name);
1128 Ok(())
1129 }
1130
1131 pub(crate) fn set_local(
1145 &mut self,
1146 node: NodeKey,
1147 name: &str,
1148 value: ParameterValue,
1149 ) -> Result<(), ParameterError> {
1150 let Some(idx) = self.find_index(node, name) else {
1151 return Err(ParameterError::NotFound);
1152 };
1153 let Some(entry) = self.table.entries[idx].as_ref() else {
1154 return Err(ParameterError::NotFound);
1155 };
1156 let dynamic = entry.descriptor.as_ref().is_some_and(|d| d.dynamic_typing);
1157 if !dynamic && entry.param.value.param_type() != value.param_type() {
1158 return Err(ParameterError::TypeMismatch);
1159 }
1160 if !self.run_on_set_callbacks(node, name, Some(&entry.param.value), Some(&value)) {
1161 return Err(ParameterError::Rejected);
1162 }
1163 if let Some(entry) = self.table.entries[idx].as_mut() {
1164 entry.param.value = value;
1165 }
1166 self.note_event(node, ParameterEventKind::Changed, name);
1167 Ok(())
1168 }
1169
1170 fn run_on_set_callbacks(
1174 &self,
1175 node: NodeKey,
1176 name: &str,
1177 old: Option<&ParameterValue>,
1178 new: Option<&ParameterValue>,
1179 ) -> bool {
1180 for slot in self.on_set.iter().flatten() {
1181 if slot.node == node && !(slot.callback)(node, name, old, new, slot.ctx) {
1182 return false;
1183 }
1184 }
1185 true
1186 }
1187
1188 pub(crate) fn add_on_set_parameters_callback(
1200 &mut self,
1201 node: NodeKey,
1202 callback: OnSetParameterFn,
1203 ctx: OnSetContext,
1204 ) -> Option<OnSetParameterHandle> {
1205 let idx = self.on_set.iter().position(Option::is_none)?;
1206 self.on_set[idx] = Some(OnSetCallbackSlot {
1207 node,
1208 callback,
1209 ctx,
1210 });
1211 Some(OnSetParameterHandle(idx as u16))
1212 }
1213
1214 pub(crate) fn remove_on_set_parameters_callback(
1217 &mut self,
1218 handle: OnSetParameterHandle,
1219 ) -> bool {
1220 let idx = handle.0 as usize;
1221 if idx >= MAX_ON_SET_CALLBACKS || self.on_set[idx].is_none() {
1222 return false;
1223 }
1224 self.on_set[idx] = None;
1225 true
1226 }
1227
1228 pub(crate) fn on_set_callbacks_free(&self) -> usize {
1231 self.on_set.iter().filter(|s| s.is_none()).count()
1232 }
1233
1234 fn descriptor_mut(&mut self, node: NodeKey, name: &str) -> Option<&mut ParameterDescriptor> {
1251 let idx = self.find_index(node, name)?;
1252 let entry = self.table.entries[idx].as_mut()?;
1253 if entry.descriptor.is_none() {
1254 entry.descriptor = Some(ParameterDescriptor::new(
1255 name,
1256 entry.param.value.param_type(),
1257 )?);
1258 }
1259 entry.descriptor.as_mut()
1260 }
1261
1262 pub(crate) fn set_parameter_description(
1266 &mut self,
1267 node: NodeKey,
1268 name: &str,
1269 description: &str,
1270 additional_constraints: &str,
1271 ) -> bool {
1272 let mut truncated = false;
1273 let found = match self.descriptor_mut(node, name) {
1274 Some(desc) => {
1275 truncated |= desc.set_description(description);
1276 truncated |= desc.set_additional_constraints(additional_constraints);
1277 true
1278 }
1279 None => false,
1280 };
1281 self.truncation_pending |= truncated;
1284 found
1285 }
1286
1287 pub(crate) fn set_parameter_read_only(
1291 &mut self,
1292 node: NodeKey,
1293 name: &str,
1294 read_only: bool,
1295 ) -> bool {
1296 match self.descriptor_mut(node, name) {
1297 Some(desc) => {
1298 desc.read_only = read_only;
1299 true
1300 }
1301 None => false,
1302 }
1303 }
1304
1305 pub(crate) fn set_parameter_integer_range(
1313 &mut self,
1314 node: NodeKey,
1315 name: &str,
1316 min: i64,
1317 max: i64,
1318 step: i64,
1319 ) -> bool {
1320 self.set_parameter_range(
1321 node,
1322 name,
1323 ParameterRange::Integer(IntegerRange::new(min, max, step)),
1324 )
1325 }
1326
1327 pub(crate) fn set_parameter_float_range(
1331 &mut self,
1332 node: NodeKey,
1333 name: &str,
1334 min: f64,
1335 max: f64,
1336 step: f64,
1337 ) -> bool {
1338 self.set_parameter_range(
1339 node,
1340 name,
1341 ParameterRange::FloatingPoint(FloatingPointRange::new(min, max, step)),
1342 )
1343 }
1344
1345 fn set_parameter_range(&mut self, node: NodeKey, name: &str, range: ParameterRange) -> bool {
1347 if !range.is_valid() {
1348 return false;
1349 }
1350 let idx = match self.find_index(node, name) {
1351 Some(idx) => idx,
1352 None => return false,
1353 };
1354 match self.table.entries[idx].as_ref() {
1359 Some(entry) if range.contains(&entry.param.value) => {}
1360 _ => return false,
1361 }
1362 match self.descriptor_mut(node, name) {
1363 Some(desc) => {
1364 desc.range = range;
1365 true
1366 }
1367 None => false,
1368 }
1369 }
1370
1371 pub(crate) fn apply_atomically<'x, I>(
1397 &mut self,
1398 node: NodeKey,
1399 items: I,
1400 ) -> Result<(), ParameterError>
1401 where
1402 I: Iterator<Item = Option<(&'x str, ParameterValue)>> + Clone,
1403 {
1404 let count = items.clone().count();
1408 let mut it = items.clone();
1409 self.apply_atomically_with(node, count, |i, slot| {
1410 if i == 0 {
1411 it = items.clone();
1412 }
1413 let (name, value) = it.next()??;
1414 slot.assign_from(&value);
1415 Some(name)
1416 })
1417 }
1418
1419 pub(crate) fn apply_atomically_with<'x>(
1432 &mut self,
1433 node: NodeKey,
1434 count: usize,
1435 mut fill: impl FnMut(usize, &mut ParameterValue) -> Option<&'x str>,
1436 ) -> Result<(), ParameterError> {
1437 let mut slot = ParameterValue::NotSet;
1438 for i in 0..count {
1439 let Some(name) = fill(i, &mut slot) else {
1440 return Err(ParameterError::TypeMismatch);
1441 };
1442 self.check_apply(node, name, &slot)?;
1443 if !self.run_on_set_callbacks(node, name, self.get(node, name), Some(&slot)) {
1449 return Err(ParameterError::Rejected);
1450 }
1451 }
1452 for i in 0..count {
1453 let Some(name) = fill(i, &mut slot) else {
1454 continue;
1455 };
1456 let _ = self.apply_ref(node, name, &slot);
1457 }
1458 Ok(())
1459 }
1460
1461 pub(crate) fn get(&self, node: NodeKey, name: &str) -> Option<&ParameterValue> {
1463 self.find_index(node, name)
1464 .and_then(|idx| self.table.entries[idx].as_ref())
1465 .map(|entry| &entry.param.value)
1466 }
1467
1468 pub(crate) fn get_parameter(&self, node: NodeKey, name: &str) -> Option<&Parameter> {
1470 self.find_index(node, name)
1471 .and_then(|idx| self.table.entries[idx].as_ref())
1472 .map(|entry| &entry.param)
1473 }
1474
1475 pub(crate) fn get_descriptor(&self, node: NodeKey, name: &str) -> Option<&ParameterDescriptor> {
1477 self.find_index(node, name)
1478 .and_then(|idx| self.table.entries[idx].as_ref())
1479 .and_then(|entry| entry.descriptor.as_ref())
1480 }
1481
1482 pub(crate) fn set(
1486 &mut self,
1487 node: NodeKey,
1488 name: &str,
1489 value: ParameterValue,
1490 ) -> Result<(), ParameterError> {
1491 self.set_ref(node, name, &value)
1492 }
1493
1494 pub(crate) fn set_ref(
1497 &mut self,
1498 node: NodeKey,
1499 name: &str,
1500 value: &ParameterValue,
1501 ) -> Result<(), ParameterError> {
1502 let idx = match self.find_index(node, name) {
1503 Some(idx) => idx,
1504 None => return Err(ParameterError::NotFound),
1505 };
1506
1507 let entry = match self.table.entries[idx].as_mut() {
1508 Some(e) => e,
1509 None => return Err(ParameterError::NotFound),
1510 };
1511
1512 Self::check_set_existing(entry, value)?;
1515
1516 entry.param.value.assign_from(value);
1517 self.note_event(node, ParameterEventKind::Changed, name);
1518 Ok(())
1519 }
1520
1521 pub(crate) fn unset(&mut self, node: NodeKey, name: &str) -> Result<(), ParameterError> {
1526 let idx = match self.find_index(node, name) {
1527 Some(idx) => idx,
1528 None => return Err(ParameterError::NotFound),
1529 };
1530
1531 let entry = match self.table.entries[idx].as_mut() {
1532 Some(e) => e,
1533 None => return Err(ParameterError::NotFound),
1534 };
1535
1536 if let Some(ref desc) = entry.descriptor
1538 && desc.read_only
1539 {
1540 return Err(ParameterError::ReadOnly);
1541 }
1542
1543 entry.param.value = ParameterValue::NotSet;
1544 self.note_event(node, ParameterEventKind::Changed, name);
1545 Ok(())
1546 }
1547
1548 pub(crate) fn set_or_declare(
1557 &mut self,
1558 node: NodeKey,
1559 name: &str,
1560 value: ParameterValue,
1561 ) -> Result<(), ParameterError> {
1562 if self.find_index(node, name).is_some() {
1563 self.set(node, name, value)
1564 } else if self.declare(node, name, value) {
1565 Ok(())
1566 } else {
1567 Err(ParameterError::StorageFull)
1568 }
1569 }
1570
1571 pub(crate) fn has(&self, node: NodeKey, name: &str) -> bool {
1573 self.find_index(node, name).is_some()
1574 }
1575
1576 pub(crate) fn remove(&mut self, node: NodeKey, name: &str) -> bool {
1580 if let Some(idx) = self.find_index(node, name) {
1581 self.table.entries[idx] = None;
1582 self.count -= 1;
1583 self.note_event(node, ParameterEventKind::Deleted, name);
1584 true
1585 } else {
1586 false
1587 }
1588 }
1589
1590 pub(crate) fn get_type(&self, node: NodeKey, name: &str) -> Option<ParameterType> {
1592 self.get(node, name).map(|v| v.param_type())
1593 }
1594
1595 pub(crate) fn iter(&self, node: NodeKey) -> impl Iterator<Item = &Parameter> {
1597 self.entries_of(node).map(|e| &e.param)
1598 }
1599
1600 pub(crate) fn list_names(&self, node: NodeKey) -> impl Iterator<Item = &str> {
1602 self.iter(node).map(|p| p.name.as_str())
1603 }
1604
1605 pub(crate) fn list_with_prefix<'a>(
1607 &'a self,
1608 node: NodeKey,
1609 prefix: &'a str,
1610 ) -> impl Iterator<Item = &'a str> {
1611 self.list_names(node)
1612 .filter(move |name| name.starts_with(prefix))
1613 }
1614
1615 pub(crate) fn get_bool(&self, node: NodeKey, name: &str) -> Option<bool> {
1617 self.get(node, name).and_then(|v| v.as_bool())
1618 }
1619
1620 pub(crate) fn get_integer(&self, node: NodeKey, name: &str) -> Option<i64> {
1622 self.get(node, name).and_then(|v| v.as_integer())
1623 }
1624
1625 pub(crate) fn get_double(&self, node: NodeKey, name: &str) -> Option<f64> {
1627 self.get(node, name).and_then(|v| v.as_double())
1628 }
1629
1630 pub(crate) fn get_string(&self, node: NodeKey, name: &str) -> Option<&str> {
1632 self.get(node, name).and_then(|v| v.as_string())
1633 }
1634
1635 pub(crate) fn set_bool(
1637 &mut self,
1638 node: NodeKey,
1639 name: &str,
1640 value: bool,
1641 ) -> Result<(), ParameterError> {
1642 self.set(node, name, ParameterValue::Bool(value))
1643 }
1644
1645 pub(crate) fn set_integer(
1647 &mut self,
1648 node: NodeKey,
1649 name: &str,
1650 value: i64,
1651 ) -> Result<(), ParameterError> {
1652 self.set(node, name, ParameterValue::Integer(value))
1653 }
1654
1655 pub(crate) fn set_double(
1657 &mut self,
1658 node: NodeKey,
1659 name: &str,
1660 value: f64,
1661 ) -> Result<(), ParameterError> {
1662 self.set(node, name, ParameterValue::Double(value))
1663 }
1664
1665 pub(crate) fn set_string(
1667 &mut self,
1668 node: NodeKey,
1669 name: &str,
1670 value: &str,
1671 ) -> Result<(), ParameterError> {
1672 match ParameterValue::from_string(value) {
1673 Some(v) => self.set(node, name, v),
1674 None => Err(ParameterError::StorageFull), }
1676 }
1677
1678 pub(crate) fn declare_parameter(
1688 &mut self,
1689 node: NodeKey,
1690 descriptor: ParameterDescriptor,
1691 initial_value: Option<&ParameterValue>,
1692 ) -> Result<(), ParameterError> {
1693 let name = descriptor.name.as_str();
1694
1695 if self.find_index(node, name).is_some() {
1697 return Err(ParameterError::TypeMismatch); }
1699
1700 let param_value = initial_value.cloned().unwrap_or_default();
1701
1702 if !Self::declaration_is_consistent(Some(&descriptor), ¶m_value) {
1704 return Err(ParameterError::InvalidRange);
1705 }
1706
1707 let slot = match self.find_empty_slot() {
1709 Some(idx) => idx,
1710 None => return Err(ParameterError::StorageFull),
1711 };
1712
1713 let param = Parameter::new(name, param_value).ok_or(ParameterError::StorageFull)?; self.note_descriptor(Some(&descriptor));
1716 self.table.entries[slot] = Some(ParameterEntry {
1717 node,
1718 param,
1719 descriptor: Some(descriptor),
1720 });
1721 self.count += 1;
1722 Ok(())
1723 }
1724
1725 pub(crate) fn get_parameter_value(&self, node: NodeKey, name: &str) -> Option<ParameterValue> {
1730 self.get(node, name).cloned()
1731 }
1732
1733 pub(crate) fn set_parameter_value(
1738 &mut self,
1739 node: NodeKey,
1740 name: &str,
1741 value: ParameterValue,
1742 ) -> Result<(), ParameterError> {
1743 self.set(node, name, value)
1744 }
1745}
1746
1747pub struct LegacyParameterBuilder<'a, 's> {
1749 server: &'a mut ParameterServer<'s>,
1750 node: NodeKey,
1753 name: String<MAX_PARAM_NAME_LEN>,
1754 value: ParameterValue,
1755 descriptor: Option<ParameterDescriptor>,
1756}
1757
1758impl<'a, 's> LegacyParameterBuilder<'a, 's> {
1759 pub fn new(
1761 server: &'a mut ParameterServer<'s>,
1762 node: NodeKey,
1763 name: &str,
1764 value: ParameterValue,
1765 ) -> Option<Self> {
1766 let mut n = String::new();
1767 n.push_str(name).ok()?;
1768 Some(Self {
1769 server,
1770 node,
1771 name: n,
1772 value,
1773 descriptor: None,
1774 })
1775 }
1776
1777 pub fn description(mut self, desc: &str) -> Self {
1779 let param_type = self.value.param_type();
1780 if self.descriptor.is_none() {
1781 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1782 }
1783 if let Some(ref mut d) = self.descriptor {
1786 d.set_description(desc);
1787 }
1788 self
1789 }
1790
1791 pub fn read_only(mut self) -> Self {
1793 let param_type = self.value.param_type();
1794 if self.descriptor.is_none() {
1795 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1796 }
1797 if let Some(ref mut d) = self.descriptor {
1798 d.read_only = true;
1799 }
1800 self
1801 }
1802
1803 pub fn integer_range(mut self, min: i64, max: i64, step: i64) -> Self {
1805 let param_type = self.value.param_type();
1806 if self.descriptor.is_none() {
1807 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1808 }
1809 if let Some(ref mut d) = self.descriptor {
1810 d.range = crate::types::ParameterRange::Integer(crate::types::IntegerRange::new(
1811 min, max, step,
1812 ));
1813 }
1814 self
1815 }
1816
1817 pub fn float_range(mut self, min: f64, max: f64, step: f64) -> Self {
1819 let param_type = self.value.param_type();
1820 if self.descriptor.is_none() {
1821 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1822 }
1823 if let Some(ref mut d) = self.descriptor {
1824 d.range = crate::types::ParameterRange::FloatingPoint(
1825 crate::types::FloatingPointRange::new(min, max, step),
1826 );
1827 }
1828 self
1829 }
1830
1831 #[must_use]
1836 pub fn declare(self) -> bool {
1837 self.server.declare_with_descriptor(
1838 self.node,
1839 self.name.as_str(),
1840 self.value,
1841 self.descriptor,
1842 )
1843 }
1844}
1845
1846#[cfg(test)]
1847mod tests {
1848 use super::*;
1849
1850 const NODE: NodeKey = NodeKey::PRIMARY;
1852 use crate::types::{ParameterDescriptor, ParameterType};
1853
1854 fn every_variant() -> std::vec::Vec<ParameterValue> {
1857 let mut strings = ParameterValue::StringArray(Default::default());
1858 if let ParameterValue::StringArray(v) = &mut strings {
1859 for e in ["a", "", "frame_id"] {
1860 let mut s = heapless::String::new();
1861 s.push_str(e).unwrap();
1862 v.push(s).unwrap();
1863 }
1864 }
1865 std::vec![
1866 ParameterValue::Bool(true),
1867 ParameterValue::Integer(-7),
1868 ParameterValue::Double(2.5),
1869 ParameterValue::from_string("base_link").unwrap(),
1870 ParameterValue::from_byte_array(&[1, 2, 3]).unwrap(),
1871 ParameterValue::from_bool_array(&[true, false]).unwrap(),
1872 ParameterValue::from_integer_array(&[4, 5, 6]).unwrap(),
1873 ParameterValue::from_double_array(&[0.5, -1.0]).unwrap(),
1874 strings,
1875 ]
1876 }
1877
1878 #[test]
1881 fn a_borrowed_declaration_stores_the_value_it_was_lent() {
1882 let values = every_variant();
1883 let mut storage = ParameterStorage::<16>::new();
1884 let mut server = ParameterServer::new_in(storage.as_table());
1885 for (i, v) in values.iter().enumerate() {
1886 let name = std::format!("p{i}");
1887 assert!(server.declare_ref(NODE, &name, v));
1888 assert_eq!(
1889 std::format!("{:?}", server.get(NODE, &name).unwrap()),
1890 std::format!("{v:?}")
1891 );
1892 }
1893 assert_eq!(server.len(NODE), values.len());
1894 assert!(!server.declare_ref(NODE, "p0", &ParameterValue::Integer(1)));
1896 assert!(matches!(
1897 server.get(NODE, "p0"),
1898 Some(ParameterValue::Bool(true))
1899 ));
1900 }
1901
1902 #[test]
1905 fn assign_from_replaces_any_previous_value() {
1906 let values = every_variant();
1907 for dst_init in &values {
1908 for src in &values {
1909 let mut dst = dst_init.clone();
1910 dst.assign_from(src);
1911 assert_eq!(std::format!("{dst:?}"), std::format!("{src:?}"));
1912 }
1913 }
1914 let mut long = ParameterValue::from_integer_array(&[1, 2, 3, 4]).unwrap();
1915 long.assign_from(&ParameterValue::from_integer_array(&[9]).unwrap());
1916 assert_eq!(
1917 std::format!("{long:?}"),
1918 std::format!("{:?}", ParameterValue::from_integer_array(&[9]).unwrap())
1919 );
1920 }
1921
1922 #[test]
1930 fn each_truncated_description_is_handed_over_once() {
1931 let long = "y".repeat(crate::MAX_PARAM_DESCRIPTION_LEN + 1);
1932 let other = NodeKey::new(1);
1933 let mut storage = ParameterStorage::<8>::new();
1934 let mut server = ParameterServer::new_in(storage.as_table());
1935 assert_eq!(server.take_truncated_descriptions(|_, _| ()), 0);
1936
1937 let d = ParameterDescriptor::new("plain", ParameterType::Integer)
1938 .unwrap()
1939 .with_description(&long);
1940 assert!(server.declare_with_descriptor(NODE, "plain", ParameterValue::Integer(1), Some(d)));
1941 let fits = ParameterDescriptor::new("short", ParameterType::Integer)
1942 .unwrap()
1943 .with_description("");
1944 assert!(server.declare_with_descriptor(
1945 NODE,
1946 "short",
1947 ParameterValue::Integer(1),
1948 Some(fits)
1949 ));
1950 crate::ParameterBuilder::<i64>::new(&mut server, other, "typed")
1951 .default(3)
1952 .description(&long)
1953 .optional()
1954 .expect("a long description no longer refuses the declaration");
1955 assert!(
1956 LegacyParameterBuilder::new(&mut server, NODE, "legacy", ParameterValue::Bool(true))
1957 .unwrap()
1958 .description(&long)
1959 .declare()
1960 );
1961
1962 let mut seen: heapless::Vec<(usize, heapless::String<16>), 8> = heapless::Vec::new();
1963 let n = server.take_truncated_descriptions(|node, name| {
1964 let mut s = heapless::String::new();
1965 s.push_str(name).unwrap();
1966 seen.push((node.index(), s)).unwrap();
1967 });
1968 assert_eq!(n, 3, "{seen:?}");
1969 for (node, name) in [(0, "plain"), (1, "typed"), (0, "legacy")] {
1970 assert!(
1971 seen.iter().any(|(k, s)| *k == node && s.as_str() == name),
1972 "{name} on node {node} not reported: {seen:?}"
1973 );
1974 }
1975 assert!(!seen.iter().any(|(_, s)| s.as_str() == "short"));
1976 assert_eq!(
1977 server.take_truncated_descriptions(|_, _| ()),
1978 0,
1979 "once per parameter"
1980 );
1981 let kept = server.get_descriptor(other, "typed").unwrap();
1982 assert_eq!(kept.description.len(), crate::MAX_PARAM_DESCRIPTION_LEN);
1983 }
1984
1985 #[test]
1992 fn two_nodes_hold_independent_values_for_one_name() {
1993 let talker = NodeKey::new(0);
1994 let listener = NodeKey::new(1);
1995 let mut storage: ParameterStorage = ParameterStorage::new();
1996 let mut server = ParameterServer::new_in(storage.as_table());
1997
1998 assert!(server.declare(talker, "rate", ParameterValue::Integer(10)));
1999 assert!(
2001 server.declare(listener, "rate", ParameterValue::Integer(20)),
2002 "a sibling node's identical name is a DIFFERENT parameter"
2003 );
2004
2005 assert_eq!(server.get_integer(talker, "rate"), Some(10));
2006 assert_eq!(server.get_integer(listener, "rate"), Some(20));
2007
2008 assert_eq!(
2010 server.set(talker, "rate", ParameterValue::Integer(11)),
2011 Ok(())
2012 );
2013 assert_eq!(server.get_integer(talker, "rate"), Some(11));
2014 assert_eq!(server.get_integer(listener, "rate"), Some(20));
2015
2016 assert_eq!(server.len(talker), 1);
2018 assert_eq!(server.len(listener), 1);
2019 assert_eq!(server.total_len(), 2, "one shared arena, two entries");
2020 }
2021
2022 #[test]
2024 fn a_sibling_cannot_read_or_write_a_name_it_never_declared() {
2025 let a = NodeKey::new(0);
2026 let b = NodeKey::new(3);
2027 let mut storage: ParameterStorage = ParameterStorage::new();
2028 let mut server = ParameterServer::new_in(storage.as_table());
2029 assert!(server.declare(a, "gain", ParameterValue::Double(1.0)));
2030
2031 assert!(!server.has(b, "gain"));
2032 assert!(server.get(b, "gain").is_none());
2033 assert_eq!(
2034 server.set(b, "gain", ParameterValue::Double(2.0)),
2035 Err(ParameterError::NotFound)
2036 );
2037 assert_eq!(
2038 server.check_apply(b, "gain", &ParameterValue::Double(2.0)),
2039 Err(ParameterError::Undeclared)
2040 );
2041 assert_eq!(server.get_double(a, "gain"), Some(1.0));
2043 }
2044
2045 #[test]
2049 fn allow_undeclared_does_not_leak_to_a_sibling_node() {
2050 let permissive = NodeKey::new(1);
2051 let strict = NodeKey::new(2);
2052 let mut storage: ParameterStorage = ParameterStorage::new();
2053 let mut server = ParameterServer::new_in(storage.as_table());
2054 server.set_allow_undeclared(permissive, true);
2055
2056 assert!(server.allows_undeclared(permissive));
2057 assert!(!server.allows_undeclared(strict));
2058
2059 assert_eq!(
2060 server.apply(permissive, "fresh", ParameterValue::Integer(7)),
2061 Ok(())
2062 );
2063 assert_eq!(
2064 server.apply(strict, "fresh", ParameterValue::Integer(7)),
2065 Err(ParameterError::Undeclared),
2066 "the sibling still refuses a name IT never declared"
2067 );
2068 assert_eq!(server.get_integer(permissive, "fresh"), Some(7));
2069 assert!(!server.has(strict, "fresh"));
2070 }
2071
2072 #[test]
2076 fn the_allow_undeclared_flag_covers_the_whole_key_space() {
2077 let mut storage: ParameterStorage = ParameterStorage::new();
2078 let mut server = ParameterServer::new_in(storage.as_table());
2079 for raw in [0u8, 63, 64, 127, 128, 191, 192, 255] {
2080 let node = NodeKey::new(raw);
2081 assert!(!server.allows_undeclared(node), "raw {raw}");
2082 server.set_allow_undeclared(node, true);
2083 assert!(server.allows_undeclared(node), "raw {raw}");
2084 server.set_allow_undeclared(node, false);
2085 assert!(!server.allows_undeclared(node), "raw {raw}");
2086 }
2087 }
2088
2089 #[test]
2093 fn the_slot_arena_is_shared_across_nodes() {
2094 let a = NodeKey::new(0);
2095 let b = NodeKey::new(1);
2096 let mut storage = ParameterStorage::<2>::new();
2097 let mut server = ParameterServer::new_in(storage.as_table());
2098 assert!(server.declare(a, "one", ParameterValue::Integer(1)));
2099 assert!(server.declare(b, "one", ParameterValue::Integer(2)));
2100 assert!(server.is_full());
2101 assert!(!server.declare(a, "two", ParameterValue::Integer(3)));
2102 assert_eq!(server.total_len(), 2);
2103 assert_eq!(server.len(a), 1);
2104 assert_eq!(server.len(b), 1);
2105 }
2106
2107 #[test]
2109 fn remove_is_keyed_by_node() {
2110 let a = NodeKey::new(0);
2111 let b = NodeKey::new(1);
2112 let mut storage: ParameterStorage = ParameterStorage::new();
2113 let mut server = ParameterServer::new_in(storage.as_table());
2114 assert!(server.declare(a, "x", ParameterValue::Integer(1)));
2115 assert!(server.declare(b, "x", ParameterValue::Integer(2)));
2116
2117 assert!(server.remove(a, "x"));
2118 assert!(!server.has(a, "x"));
2119 assert_eq!(server.get_integer(b, "x"), Some(2));
2120 assert_eq!(server.total_len(), 1);
2121 }
2122
2123 #[test]
2126 fn set_rejects_a_value_off_the_integer_step() {
2127 let mut storage: ParameterStorage = ParameterStorage::new();
2128 let mut server = ParameterServer::new_in(storage.as_table());
2129 let desc = ParameterDescriptor::new("rate", ParameterType::Integer)
2130 .unwrap()
2131 .with_integer_range(0, 100, 10);
2132 assert!(server.declare_with_descriptor(
2133 NODE,
2134 "rate",
2135 ParameterValue::Integer(20),
2136 Some(desc)
2137 ));
2138
2139 assert_eq!(
2140 server.set(NODE, "rate", ParameterValue::Integer(30)),
2141 Ok(())
2142 );
2143 assert_eq!(
2145 server.set(NODE, "rate", ParameterValue::Integer(35)),
2146 Err(ParameterError::OutOfRange)
2147 );
2148 assert_eq!(server.get_integer(NODE, "rate"), Some(30));
2149 }
2150
2151 #[test]
2152 fn set_rejects_a_value_off_the_float_step() {
2153 let mut storage: ParameterStorage = ParameterStorage::new();
2154 let mut server = ParameterServer::new_in(storage.as_table());
2155 let desc = ParameterDescriptor::new("gain", ParameterType::Double)
2156 .unwrap()
2157 .with_float_range(0.0, 1.0, 0.25);
2158 assert!(server.declare_with_descriptor(
2159 NODE,
2160 "gain",
2161 ParameterValue::Double(0.5),
2162 Some(desc)
2163 ));
2164
2165 assert_eq!(server.set_double(NODE, "gain", 0.75), Ok(()));
2166 assert_eq!(
2167 server.set_double(NODE, "gain", 0.6),
2168 Err(ParameterError::OutOfRange)
2169 );
2170 assert_eq!(server.get_double(NODE, "gain"), Some(0.75));
2171 }
2172
2173 #[test]
2174 fn declare_refuses_an_ill_formed_range() {
2175 let mut storage: ParameterStorage = ParameterStorage::new();
2176 let mut server = ParameterServer::new_in(storage.as_table());
2177 let desc = ParameterDescriptor::new("bad", ParameterType::Integer)
2178 .unwrap()
2179 .with_integer_range(0, 100, -1);
2180 assert!(!server.declare_with_descriptor(
2181 NODE,
2182 "bad",
2183 ParameterValue::Integer(0),
2184 Some(desc.clone())
2185 ));
2186 assert!(!server.has(NODE, "bad"));
2187 assert_eq!(
2188 server.declare_parameter(NODE, desc, Some(&ParameterValue::Integer(0))),
2189 Err(ParameterError::InvalidRange)
2190 );
2191 assert!(!server.has(NODE, "bad"));
2192 }
2193
2194 #[test]
2195 fn declare_refuses_a_default_its_own_range_rejects() {
2196 let mut storage: ParameterStorage = ParameterStorage::new();
2197 let mut server = ParameterServer::new_in(storage.as_table());
2198 let desc = ParameterDescriptor::new("odd", ParameterType::Integer)
2199 .unwrap()
2200 .with_integer_range(0, 100, 2);
2201 assert!(!server.declare_with_descriptor(
2202 NODE,
2203 "odd",
2204 ParameterValue::Integer(3),
2205 Some(desc.clone())
2206 ));
2207 assert!(server.declare_with_descriptor(
2208 NODE,
2209 "odd",
2210 ParameterValue::Integer(4),
2211 Some(desc)
2212 ));
2213 }
2214
2215 #[test]
2218 fn apply_rejects_an_undeclared_name_by_default() {
2219 let mut storage: ParameterStorage = ParameterStorage::new();
2220 let mut server = ParameterServer::new_in(storage.as_table());
2221 assert!(server.declare(NODE, "max_speed", ParameterValue::Double(1.0)));
2222 assert!(!server.allows_undeclared(NODE));
2223
2224 assert_eq!(
2227 server.check_apply(NODE, "max_speeed", &ParameterValue::Double(5.0)),
2228 Err(ParameterError::Undeclared)
2229 );
2230 assert_eq!(
2231 server.apply(NODE, "max_speeed", ParameterValue::Double(5.0)),
2232 Err(ParameterError::Undeclared)
2233 );
2234 assert!(!server.has(NODE, "max_speeed"));
2235 assert_eq!(server.len(NODE), 1);
2236 assert_eq!(server.get_double(NODE, "max_speed"), Some(1.0));
2237
2238 assert_eq!(
2240 server.apply(NODE, "max_speed", ParameterValue::Double(5.0)),
2241 Ok(())
2242 );
2243 assert_eq!(server.get_double(NODE, "max_speed"), Some(5.0));
2244 }
2245
2246 #[test]
2247 fn apply_declares_an_unknown_name_under_allow_undeclared() {
2248 let mut storage: ParameterStorage = ParameterStorage::new();
2249 let mut server = ParameterServer::new_in(storage.as_table());
2250 server.set_allow_undeclared(NODE, true);
2251 assert!(server.allows_undeclared(NODE));
2252
2253 assert_eq!(
2254 server.check_apply(NODE, "fresh", &ParameterValue::Integer(7)),
2255 Ok(())
2256 );
2257 assert_eq!(
2258 server.apply(NODE, "fresh", ParameterValue::Integer(7)),
2259 Ok(())
2260 );
2261 assert_eq!(server.get_integer(NODE, "fresh"), Some(7));
2262 }
2263
2264 #[test]
2265 fn apply_under_allow_undeclared_still_reports_a_full_store() {
2266 let mut storage: ParameterStorage<1> = ParameterStorage::new();
2267 let mut server = ParameterServer::new_in(storage.as_table());
2268 server.set_allow_undeclared(NODE, true);
2269 assert!(server.declare(NODE, "only", ParameterValue::Bool(true)));
2270 assert_eq!(
2271 server.check_apply(NODE, "second", &ParameterValue::Bool(true)),
2272 Err(ParameterError::StorageFull)
2273 );
2274 assert_eq!(
2275 server.apply(NODE, "second", ParameterValue::Bool(true)),
2276 Err(ParameterError::StorageFull)
2277 );
2278 }
2279
2280 #[test]
2281 fn apply_enforces_the_existing_entry_rules_through_the_same_predicate() {
2282 let mut storage: ParameterStorage = ParameterStorage::new();
2283 let mut server = ParameterServer::new_in(storage.as_table());
2284 let desc = ParameterDescriptor::new("rate", ParameterType::Integer)
2285 .unwrap()
2286 .with_integer_range(0, 100, 10);
2287 assert!(server.declare_with_descriptor(
2288 NODE,
2289 "rate",
2290 ParameterValue::Integer(20),
2291 Some(desc)
2292 ));
2293 assert_eq!(
2294 server.check_apply(NODE, "rate", &ParameterValue::Integer(35)),
2295 Err(ParameterError::OutOfRange)
2296 );
2297 assert_eq!(
2298 server.apply(NODE, "rate", ParameterValue::Integer(35)),
2299 Err(ParameterError::OutOfRange)
2300 );
2301 assert_eq!(
2302 server.apply(NODE, "rate", ParameterValue::Double(30.0)),
2303 Err(ParameterError::TypeMismatch)
2304 );
2305 assert_eq!(server.get_integer(NODE, "rate"), Some(20));
2306 }
2307
2308 #[test]
2309 fn test_new_server() {
2310 let mut storage: ParameterStorage = ParameterStorage::new();
2311 let server = ParameterServer::new_in(storage.as_table());
2312 assert_eq!(server.len(NODE), 0);
2313 assert!(server.is_empty(NODE));
2314 }
2315
2316 #[test]
2317 #[allow(clippy::approx_constant)]
2318 fn test_declare_and_get() {
2319 let mut storage: ParameterStorage = ParameterStorage::new();
2320 let mut server = ParameterServer::new_in(storage.as_table());
2321
2322 assert!(server.declare(NODE, "my_bool", ParameterValue::Bool(true)));
2323 assert!(server.declare(NODE, "my_int", ParameterValue::Integer(42)));
2324 assert!(server.declare(NODE, "my_double", ParameterValue::Double(3.14)));
2325
2326 assert_eq!(server.len(NODE), 3);
2327 assert!(!server.is_empty(NODE));
2328
2329 assert_eq!(server.get_bool(NODE, "my_bool"), Some(true));
2330 assert_eq!(server.get_integer(NODE, "my_int"), Some(42));
2331 assert_eq!(server.get_double(NODE, "my_double"), Some(3.14));
2332 }
2333
2334 #[test]
2335 fn test_unset_clears_the_value_and_reports_success() {
2336 let mut storage: ParameterStorage = ParameterStorage::new();
2342 let mut server = ParameterServer::new_in(storage.as_table());
2343 assert!(server.declare(NODE, "count", ParameterValue::Integer(0)));
2344 assert_eq!(server.set_integer(NODE, "count", 10), Ok(()));
2345 assert_eq!(server.get_integer(NODE, "count"), Some(10));
2346
2347 assert_eq!(server.unset(NODE, "count"), Ok(()));
2348 assert_eq!(server.get_integer(NODE, "count"), None);
2349 }
2350
2351 #[test]
2352 fn test_set_parameter() {
2353 let mut storage: ParameterStorage = ParameterStorage::new();
2354 let mut server = ParameterServer::new_in(storage.as_table());
2355 assert!(server.declare(NODE, "count", ParameterValue::Integer(0)));
2356
2357 assert_eq!(server.set_integer(NODE, "count", 10), Ok(()));
2358 assert_eq!(server.get_integer(NODE, "count"), Some(10));
2359 }
2360
2361 #[test]
2362 fn test_set_nonexistent() {
2363 let mut storage: ParameterStorage = ParameterStorage::new();
2364 let mut server = ParameterServer::new_in(storage.as_table());
2365 assert_eq!(
2366 server.set(NODE, "nonexistent", ParameterValue::Integer(1)),
2367 Err(ParameterError::NotFound)
2368 );
2369 }
2370
2371 #[test]
2372 fn test_read_only_parameter() {
2373 let mut storage: ParameterStorage = ParameterStorage::new();
2374 let mut server = ParameterServer::new_in(storage.as_table());
2375
2376 let desc = ParameterDescriptor::new("version", ParameterType::String)
2377 .unwrap()
2378 .with_read_only(true);
2379
2380 assert!(server.declare_with_descriptor(
2381 NODE,
2382 "version",
2383 ParameterValue::from_string("1.0.0").unwrap(),
2384 Some(desc),
2385 ));
2386
2387 assert_eq!(
2388 server.set_string(NODE, "version", "2.0.0"),
2389 Err(ParameterError::ReadOnly)
2390 );
2391 assert_eq!(server.get_string(NODE, "version"), Some("1.0.0"));
2392 }
2393
2394 #[test]
2395 fn test_range_constraints() {
2396 let mut storage: ParameterStorage = ParameterStorage::new();
2397 let mut server = ParameterServer::new_in(storage.as_table());
2398
2399 let desc = ParameterDescriptor::new("speed", ParameterType::Double)
2400 .unwrap()
2401 .with_float_range(0.0, 10.0, 0.0);
2402
2403 assert!(server.declare_with_descriptor(
2404 NODE,
2405 "speed",
2406 ParameterValue::Double(5.0),
2407 Some(desc)
2408 ));
2409
2410 assert_eq!(server.set_double(NODE, "speed", 8.0), Ok(()));
2412
2413 assert_eq!(
2415 server.set_double(NODE, "speed", 15.0),
2416 Err(ParameterError::OutOfRange)
2417 );
2418 assert_eq!(server.get_double(NODE, "speed"), Some(8.0)); }
2420
2421 #[test]
2422 fn test_remove_parameter() {
2423 let mut storage: ParameterStorage = ParameterStorage::new();
2424 let mut server = ParameterServer::new_in(storage.as_table());
2425 assert!(server.declare(NODE, "temp", ParameterValue::Double(25.0)));
2426
2427 assert!(server.has(NODE, "temp"));
2428 assert!(server.remove(NODE, "temp"));
2429 assert!(!server.has(NODE, "temp"));
2430 assert!(!server.remove(NODE, "temp")); }
2432
2433 #[test]
2434 fn test_list_parameters() {
2435 let mut storage: ParameterStorage = ParameterStorage::new();
2436 let mut server = ParameterServer::new_in(storage.as_table());
2437 assert!(server.declare(NODE, "robot.speed", ParameterValue::Double(1.0)));
2438 assert!(server.declare(
2439 NODE,
2440 "robot.name",
2441 ParameterValue::from_string("bot1").unwrap(),
2442 ));
2443 assert!(server.declare(NODE, "sensor.range", ParameterValue::Double(10.0)));
2444
2445 let robot_params: heapless::Vec<&str, 8> =
2446 server.list_with_prefix(NODE, "robot.").collect();
2447 assert_eq!(robot_params.len(), 2);
2448 }
2449
2450 #[test]
2451 fn test_set_or_declare() {
2452 let mut storage: ParameterStorage = ParameterStorage::new();
2453 let mut server = ParameterServer::new_in(storage.as_table());
2454
2455 assert_eq!(
2457 server.set_or_declare(NODE, "new_param", ParameterValue::Integer(1)),
2458 Ok(())
2459 );
2460 assert_eq!(server.get_integer(NODE, "new_param"), Some(1));
2461
2462 assert_eq!(
2464 server.set_or_declare(NODE, "new_param", ParameterValue::Integer(2)),
2465 Ok(())
2466 );
2467 assert_eq!(server.get_integer(NODE, "new_param"), Some(2));
2468 }
2469
2470 #[test]
2471 fn test_duplicate_declare() {
2472 let mut storage: ParameterStorage = ParameterStorage::new();
2473 let mut server = ParameterServer::new_in(storage.as_table());
2474 assert!(server.declare(NODE, "param", ParameterValue::Integer(1)));
2475 assert!(!server.declare(NODE, "param", ParameterValue::Integer(2))); assert_eq!(server.get_integer(NODE, "param"), Some(1)); }
2478
2479 #[test]
2485 #[allow(clippy::absurd_extreme_comparisons, clippy::unnecessary_min_or_max)]
2492 fn descriptor_verbs_attach_to_a_declared_parameter() {
2493 let mut storage: ParameterStorage = ParameterStorage::new();
2494 let mut server = ParameterServer::new_in(storage.as_table());
2495 assert!(server.declare(NODE, "rate", ParameterValue::Double(2.0)));
2496
2497 assert!(server.get_descriptor(NODE, "rate").is_none());
2500 assert!(server.set_parameter_description(NODE, "rate", "publish rate", "hz, positive"));
2501 assert!(server.set_parameter_float_range(NODE, "rate", 0.0, 10.0, 0.0));
2502
2503 let desc = server.get_descriptor(NODE, "rate").expect("descriptor");
2504 assert_eq!(desc.description.as_str(), "publish rate");
2505 let constraints_fit = "hz, positive".len() <= crate::MAX_PARAM_CONSTRAINTS_LEN;
2511 assert_eq!(
2512 desc.additional_constraints.as_str(),
2513 &"hz, positive"[..crate::MAX_PARAM_CONSTRAINTS_LEN.min("hz, positive".len())]
2514 );
2515 assert_eq!(desc.param_type, ParameterType::Double);
2516
2517 assert_eq!(
2519 server.apply(NODE, "rate", ParameterValue::Double(50.0)),
2520 Err(ParameterError::OutOfRange)
2521 );
2522 assert_eq!(
2523 server.apply(NODE, "rate", ParameterValue::Double(5.0)),
2524 Ok(())
2525 );
2526
2527 assert!(server.set_parameter_read_only(NODE, "rate", true));
2529 assert_eq!(
2530 server.apply(NODE, "rate", ParameterValue::Double(6.0)),
2531 Err(ParameterError::ReadOnly)
2532 );
2533
2534 let mut reported = 0usize;
2538 server.take_truncated_descriptions(|_, _| reported += 1);
2539 assert_eq!(
2540 reported,
2541 usize::from(!constraints_fit),
2542 "a constraints string that did not fit the capacity went unreported"
2543 );
2544
2545 assert!(!server.set_parameter_description(NODE, "absent", "d", "c"));
2548 assert!(!server.set_parameter_read_only(NODE, "absent", true));
2549 assert!(!server.set_parameter_integer_range(NODE, "absent", 0, 1, 1));
2550 }
2551
2552 #[test]
2556 fn a_range_must_fit_the_value_it_is_attached_to() {
2557 let mut storage: ParameterStorage = ParameterStorage::new();
2558 let mut server = ParameterServer::new_in(storage.as_table());
2559 assert!(server.declare(NODE, "depth", ParameterValue::Integer(100)));
2560
2561 assert!(!server.set_parameter_integer_range(NODE, "depth", 0, 10, 1));
2562 assert!(server.get_descriptor(NODE, "depth").is_none());
2563
2564 assert!(!server.set_parameter_integer_range(NODE, "depth", 10, 0, 1));
2566 assert!(!server.set_parameter_integer_range(NODE, "depth", 0, 1000, -1));
2568
2569 assert!(server.set_parameter_integer_range(NODE, "depth", 0, 1000, 1));
2570 }
2571
2572 #[test]
2575 fn undeclare_frees_the_slot_for_that_node_only() {
2576 let mut storage: ParameterStorage = ParameterStorage::new();
2577 let mut server = ParameterServer::new_in(storage.as_table());
2578 let other = NodeKey::new(1);
2579 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2580 assert!(server.declare(other, "rate", ParameterValue::Integer(2)));
2581 assert_eq!(server.total_len(), 2);
2582
2583 assert!(server.remove(NODE, "rate"));
2584 assert!(!server.has(NODE, "rate"));
2585 assert!(server.has(other, "rate"));
2586 assert_eq!(server.total_len(), 1);
2587 assert!(!server.remove(NODE, "rate"));
2588
2589 assert!(server.declare(NODE, "rate", ParameterValue::Integer(3)));
2590 assert_eq!(server.get_integer(NODE, "rate"), Some(3));
2591 }
2592
2593 fn refuse_everything(
2594 _n: NodeKey,
2595 _name: &str,
2596 _old: Option<&ParameterValue>,
2597 _new: Option<&ParameterValue>,
2598 _ctx: OnSetContext,
2599 ) -> bool {
2600 false
2601 }
2602
2603 fn refuse_if_ctx_matches(
2604 _n: NodeKey,
2605 _name: &str,
2606 _old: Option<&ParameterValue>,
2607 new: Option<&ParameterValue>,
2608 ctx: OnSetContext,
2609 ) -> bool {
2610 new.and_then(ParameterValue::as_integer) != Some(ctx.a as i64)
2611 }
2612
2613 #[test]
2617 fn an_on_set_callback_vetoes_the_write_it_refuses() {
2618 let mut storage: ParameterStorage = ParameterStorage::new();
2619 let mut server = ParameterServer::new_in(storage.as_table());
2620 let other = NodeKey::new(1);
2621 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2622 assert!(server.declare(other, "rate", ParameterValue::Integer(1)));
2623
2624 let h = server
2625 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
2626 .expect("a free slot");
2627
2628 assert_eq!(
2629 server.apply(NODE, "rate", ParameterValue::Integer(9)),
2630 Err(ParameterError::Rejected)
2631 );
2632 assert_eq!(server.get_integer(NODE, "rate"), Some(1));
2633 assert_eq!(
2635 server.apply(other, "rate", ParameterValue::Integer(9)),
2636 Ok(())
2637 );
2638
2639 assert!(server.remove_on_set_parameters_callback(h));
2640 assert!(!server.remove_on_set_parameters_callback(h));
2641 assert_eq!(
2642 server.apply(NODE, "rate", ParameterValue::Integer(9)),
2643 Ok(())
2644 );
2645 }
2646
2647 #[derive(Default)]
2649 struct Seen {
2650 calls: u32,
2651 old: Option<i64>,
2652 new: Option<i64>,
2653 old_present: bool,
2654 new_present: bool,
2655 }
2656
2657 fn record(
2658 _n: NodeKey,
2659 _name: &str,
2660 old: Option<&ParameterValue>,
2661 new: Option<&ParameterValue>,
2662 ctx: OnSetContext,
2663 ) -> bool {
2664 let seen = unsafe { &mut *(ctx.a as *mut Seen) };
2666 seen.calls += 1;
2667 seen.old_present = old.is_some();
2668 seen.new_present = new.is_some();
2669 seen.old = old.and_then(ParameterValue::as_integer);
2670 seen.new = new.and_then(ParameterValue::as_integer);
2671 true
2672 }
2673
2674 #[test]
2677 fn the_event_log_merges_by_name_and_counts_overflow() {
2678 let mut storage: ParameterStorage = ParameterStorage::new();
2679 let mut server = ParameterServer::new_in(storage.as_table());
2680 assert!(server.declare(NODE, "quiet", ParameterValue::Integer(0)));
2681 assert!(server.pop_parameter_event().is_none(), "off by default");
2682
2683 server.set_parameter_events(NODE, true);
2684 let e = server
2686 .pop_initial_parameter_event()
2687 .expect("the start-up event");
2688 assert_eq!(
2689 (e.name.as_str(), e.kind),
2690 ("quiet", ParameterEventKind::New)
2691 );
2692 assert!(server.pop_initial_parameter_event().is_none());
2693 assert!(server.declare(NODE, "a", ParameterValue::Integer(1)));
2694 assert_eq!(server.set(NODE, "a", ParameterValue::Integer(2)), Ok(()));
2695 assert!(server.remove(NODE, "a"));
2696 let e = server.pop_parameter_event().expect("one merged entry");
2697 assert_eq!(
2698 (e.name.as_str(), e.kind),
2699 ("a", ParameterEventKind::Deleted)
2700 );
2701 assert!(server.pop_parameter_event().is_none());
2702
2703 for i in 0..PARAMETER_EVENT_LOG_LEN + 2 {
2704 let mut name = heapless::String::<8>::new();
2705 core::fmt::Write::write_fmt(&mut name, format_args!("p{i}")).unwrap();
2706 assert!(server.declare(NODE, &name, ParameterValue::Integer(0)));
2707 }
2708 assert_eq!(server.take_parameter_events_dropped(), 2);
2709 assert_eq!(
2710 server.take_parameter_events_dropped(),
2711 0,
2712 "the count resets"
2713 );
2714 }
2715
2716 #[test]
2721 fn the_start_up_event_reports_what_the_node_holds() {
2722 let mut storage: ParameterStorage = ParameterStorage::new();
2723 let mut server = ParameterServer::new_in(storage.as_table());
2724 let other = NodeKey::new(1);
2725 assert!(server.declare(NODE, "a", ParameterValue::Integer(1)));
2726 assert!(server.declare(NODE, "b", ParameterValue::Integer(2)));
2727 assert!(server.declare(other, "x", ParameterValue::Integer(9)));
2728
2729 server.set_parameter_events(NODE, true);
2730 server.set_parameter_events(NodeKey::new(2), true);
2731 assert!(server.declare(NODE, "c", ParameterValue::Integer(3)));
2733 assert!(server.remove(NODE, "b"));
2734 assert!(server.pop_parameter_event().is_none(), "not logged twice");
2735
2736 let mut seen = heapless::Vec::<(u8, ParameterEventKind, String<8>), 8>::new();
2737 while let Some(e) = server.pop_initial_parameter_event() {
2738 let mut n = String::new();
2739 n.push_str(e.name.as_str()).unwrap();
2740 seen.push((e.node.raw(), e.kind, n)).unwrap();
2741 }
2742 let names: heapless::Vec<(u8, ParameterEventKind, &str), 8> =
2743 seen.iter().map(|(n, k, s)| (*n, *k, s.as_str())).collect();
2744 assert_eq!(
2745 names.as_slice(),
2746 &[
2747 (0, ParameterEventKind::New, "a"),
2748 (0, ParameterEventKind::New, "c"),
2749 (2, ParameterEventKind::Announce, ""),
2750 ],
2751 "node 1 never enabled events; node 2 holds nothing"
2752 );
2753
2754 server.set_parameter_events(NODE, true);
2756 assert!(server.pop_initial_parameter_event().is_none());
2757 assert!(server.remove(NODE, "a"));
2759 let e = server.pop_parameter_event().expect("a logged deletion");
2760 assert_eq!(
2761 (e.name.as_str(), e.kind),
2762 ("a", ParameterEventKind::Deleted)
2763 );
2764 }
2765
2766 #[test]
2769 fn the_on_set_hook_sees_old_and_new() {
2770 let mut storage: ParameterStorage = ParameterStorage::new();
2771 let mut server = ParameterServer::new_in(storage.as_table());
2772 let mut seen = Seen::default();
2773 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2774 server
2775 .add_on_set_parameters_callback(NODE, record, OnSetContext::new(&raw mut seen as usize))
2776 .expect("a free slot");
2777
2778 assert_eq!(
2779 server.apply(NODE, "rate", ParameterValue::Integer(2)),
2780 Ok(())
2781 );
2782 assert_eq!((seen.old, seen.new), (Some(1), Some(2)));
2783
2784 server.set_allow_undeclared(NODE, true);
2785 assert_eq!(
2786 server.apply(NODE, "fresh", ParameterValue::Integer(7)),
2787 Ok(())
2788 );
2789 assert!(!seen.old_present, "a create has no old value");
2790 assert_eq!(seen.new, Some(7));
2791 }
2792
2793 #[test]
2796 fn a_local_set_skips_read_only_and_range_but_not_type() {
2797 let mut storage: ParameterStorage = ParameterStorage::new();
2798 let mut server = ParameterServer::new_in(storage.as_table());
2799 let mut seen = Seen::default();
2800 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2801 assert!(server.set_parameter_integer_range(NODE, "rate", 0, 10, 1));
2802 assert!(server.set_parameter_read_only(NODE, "rate", true));
2803 server
2804 .add_on_set_parameters_callback(NODE, record, OnSetContext::new(&raw mut seen as usize))
2805 .expect("a free slot");
2806
2807 assert_eq!(
2809 server.apply(NODE, "rate", ParameterValue::Integer(50)),
2810 Err(ParameterError::ReadOnly)
2811 );
2812 assert_eq!(seen.calls, 0, "a refused remote write reached the hook");
2813
2814 assert_eq!(
2816 server.set_local(NODE, "rate", ParameterValue::Integer(50)),
2817 Ok(())
2818 );
2819 assert_eq!((seen.old, seen.new), (Some(1), Some(50)));
2820 assert_eq!(server.get_integer(NODE, "rate"), Some(50));
2821
2822 assert_eq!(
2823 server.set_local(NODE, "rate", ParameterValue::Double(1.0)),
2824 Err(ParameterError::TypeMismatch)
2825 );
2826 assert_eq!(
2827 server.set_local(NODE, "nope", ParameterValue::Integer(1)),
2828 Err(ParameterError::NotFound)
2829 );
2830 assert!(
2831 server
2832 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
2833 .is_some()
2834 );
2835 assert_eq!(
2836 server.set_local(NODE, "rate", ParameterValue::Integer(3)),
2837 Err(ParameterError::Rejected)
2838 );
2839 assert_eq!(server.get_integer(NODE, "rate"), Some(50));
2840 }
2841
2842 #[test]
2845 fn not_set_deletes_only_on_a_node_that_asked() {
2846 let mut storage: ParameterStorage = ParameterStorage::new();
2847 let mut server = ParameterServer::new_in(storage.as_table());
2848 let other = NodeKey::new(1);
2849 let mut seen = Seen::default();
2850 assert!(server.declare(NODE, "rate", ParameterValue::Integer(4)));
2851 assert!(server.declare(other, "rate", ParameterValue::Integer(4)));
2852 server.set_delete_on_not_set(NODE, true);
2853 server
2854 .add_on_set_parameters_callback(NODE, record, OnSetContext::new(&raw mut seen as usize))
2855 .expect("a free slot");
2856
2857 assert_eq!(server.apply(NODE, "rate", ParameterValue::NotSet), Ok(()));
2858 assert!(!server.has(NODE, "rate"));
2859 assert_eq!((seen.old, seen.new_present), (Some(4), false));
2860
2861 let _ = server.apply(other, "rate", ParameterValue::NotSet);
2864 assert!(
2865 server.has(other, "rate"),
2866 "the rclcpp node's parameter was deleted"
2867 );
2868
2869 assert!(server.declare(NODE, "ro", ParameterValue::Integer(1)));
2870 assert!(server.set_parameter_read_only(NODE, "ro", true));
2871 assert_eq!(
2872 server.apply(NODE, "ro", ParameterValue::NotSet),
2873 Err(ParameterError::ReadOnly)
2874 );
2875 assert!(server.has(NODE, "ro"));
2876 }
2877
2878 #[test]
2883 fn apply_staged_is_apply_from_a_slot() {
2884 fn reject(
2885 _: NodeKey,
2886 name: &str,
2887 _: Option<&ParameterValue>,
2888 _: Option<&ParameterValue>,
2889 _: OnSetContext,
2890 ) -> bool {
2891 name != "vetoed"
2892 }
2893 fn prepared(storage: &mut ParameterStorage<4>) -> ParameterServer<'_> {
2894 let mut server = ParameterServer::new_in(storage.as_table());
2895 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2896 assert!(
2897 server.declare_with_descriptor(
2898 NODE,
2899 "ro",
2900 ParameterValue::Integer(1),
2901 Some(
2902 ParameterDescriptor::new("ro", ParameterType::Integer)
2903 .unwrap()
2904 .with_read_only(true)
2905 ),
2906 )
2907 );
2908 assert!(server.declare_with_descriptor(
2909 NODE,
2910 "typed",
2911 ParameterValue::Integer(1),
2912 Some(ParameterDescriptor::new("typed", ParameterType::Integer).unwrap()),
2913 ));
2914 assert!(server.declare(NODE, "vetoed", ParameterValue::Integer(1)));
2915 server
2916 .add_on_set_parameters_callback(NODE, reject, OnSetContext::new(0))
2917 .expect("a free slot");
2918 server
2919 }
2920 type Row = (&'static str, fn() -> ParameterValue, bool, bool);
2923 let rows: [Row; 7] = [
2924 ("rate", || ParameterValue::Integer(7), false, false),
2925 ("ro", || ParameterValue::Integer(7), false, false),
2926 ("typed", || ParameterValue::Bool(true), false, false),
2927 ("vetoed", || ParameterValue::Integer(7), false, false),
2928 ("fresh", || ParameterValue::Integer(7), false, false),
2929 ("fresh", || ParameterValue::Integer(7), true, false),
2931 ("rate", || ParameterValue::NotSet, false, true),
2933 ];
2934 for (name, value, allow, delete) in rows {
2935 let mut a_storage: ParameterStorage<4> = ParameterStorage::new();
2936 let mut b_storage: ParameterStorage<4> = ParameterStorage::new();
2937 let mut by_value = prepared(&mut a_storage);
2938 let mut by_slot = prepared(&mut b_storage);
2939 for s in [&mut by_value, &mut by_slot] {
2940 s.set_allow_undeclared(NODE, allow);
2941 s.set_delete_on_not_set(NODE, delete);
2942 }
2943 let want = by_value.apply(NODE, name, value());
2944 let mut slot = value();
2945 let got = by_slot.apply_staged(NODE, name, &mut slot);
2946 assert_eq!(got, want, "verdict for `{name}`");
2947 for probe in ["rate", "ro", "typed", "vetoed", "fresh"] {
2948 assert_eq!(
2949 by_slot.get(NODE, probe).map(ParameterValue::param_type),
2950 by_value.get(NODE, probe).map(ParameterValue::param_type),
2951 "`{probe}` after setting `{name}`"
2952 );
2953 assert_eq!(
2954 by_slot.get_integer(NODE, probe),
2955 by_value.get_integer(NODE, probe),
2956 "`{probe}` after setting `{name}`"
2957 );
2958 }
2959 }
2960
2961 let mut storage: ParameterStorage<4> = ParameterStorage::new();
2963 let mut server = ParameterServer::new_in(storage.as_table());
2964 server.set_allow_undeclared(NODE, true);
2965 let mut slot = ParameterValue::Double(2.5);
2966 assert_eq!(server.apply_staged(NODE, "fresh", &mut slot), Ok(()));
2967 assert_eq!(server.get_double(NODE, "fresh"), Some(2.5));
2968 assert_eq!(server.len(NODE), 1);
2969
2970 let mut slot = ParameterValue::Double(4.0);
2972 assert!(server.apply_staged(NODE, "fresh", &mut slot).is_ok());
2973 assert_eq!(slot.as_double(), Some(2.5));
2974 assert_eq!(server.get_double(NODE, "fresh"), Some(4.0));
2975 }
2976
2977 #[test]
2981 fn the_on_set_registry_refuses_past_its_stated_capacity() {
2982 let mut storage: ParameterStorage = ParameterStorage::new();
2983 let mut server = ParameterServer::new_in(storage.as_table());
2984 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2985
2986 assert_eq!(server.on_set_callbacks_free(), MAX_ON_SET_CALLBACKS);
2987 let mut handles = heapless::Vec::<_, MAX_ON_SET_CALLBACKS>::new();
2988 for i in 0..MAX_ON_SET_CALLBACKS {
2989 let h = server
2990 .add_on_set_parameters_callback(
2991 NODE,
2992 refuse_if_ctx_matches,
2993 OnSetContext::new(40 + i),
2994 )
2995 .expect("within capacity");
2996 handles.push(h).expect("bounded by the same constant");
2997 }
2998 assert_eq!(server.on_set_callbacks_free(), 0);
2999 assert!(
3000 server
3001 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
3002 .is_none()
3003 );
3004
3005 for i in 0..MAX_ON_SET_CALLBACKS {
3007 assert_eq!(
3008 server.apply(NODE, "rate", ParameterValue::Integer(40 + i as i64)),
3009 Err(ParameterError::Rejected)
3010 );
3011 }
3012 assert_eq!(
3013 server.apply(NODE, "rate", ParameterValue::Integer(7)),
3014 Ok(())
3015 );
3016
3017 assert!(server.remove_on_set_parameters_callback(handles[0]));
3018 assert_eq!(server.on_set_callbacks_free(), 1);
3019 }
3020
3021 #[test]
3024 fn the_store_rules_run_before_the_hook() {
3025 let mut storage: ParameterStorage = ParameterStorage::new();
3026 let mut server = ParameterServer::new_in(storage.as_table());
3027 let desc = ParameterDescriptor::new("rate", ParameterType::Integer)
3028 .expect("name fits")
3029 .with_read_only(true);
3030 assert!(server.declare_with_descriptor(
3031 NODE,
3032 "rate",
3033 ParameterValue::Integer(1),
3034 Some(desc)
3035 ));
3036 let _ = server
3037 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
3038 .expect("a free slot");
3039 assert_eq!(
3041 server.apply(NODE, "rate", ParameterValue::Integer(2)),
3042 Err(ParameterError::ReadOnly)
3043 );
3044 }
3045
3046 #[test]
3050 fn the_on_set_registry_costs_what_its_doc_says() {
3051 let slot = core::mem::size_of::<Option<OnSetCallbackSlot>>();
3052 let word = core::mem::size_of::<usize>();
3053 assert!(
3056 slot <= 4 * word,
3057 "an on-set slot is {slot} bytes, more than the four words its doc claims"
3058 );
3059 assert_eq!(
3060 core::mem::size_of::<[Option<OnSetCallbackSlot>; MAX_ON_SET_CALLBACKS]>(),
3061 MAX_ON_SET_CALLBACKS * slot
3062 );
3063 }
3064
3065 #[test]
3067 fn try_declare_names_the_refusal() {
3068 let mut storage: ParameterStorage<2> = ParameterStorage::new();
3069 let mut server = ParameterServer::new_in(storage.as_table());
3070
3071 assert_eq!(
3072 server.try_declare(NODE, "a", ParameterValue::Integer(1)),
3073 Ok(())
3074 );
3075 assert_eq!(
3076 server.try_declare(NODE, "a", ParameterValue::Integer(1)),
3077 Err(DeclarationError::AlreadyDeclared)
3078 );
3079
3080 let bad_range = ParameterDescriptor::new("b", ParameterType::Integer)
3081 .expect("name fits")
3082 .with_integer_range(10, 0, 1);
3083 assert_eq!(
3084 server.try_declare_with_descriptor(
3085 NODE,
3086 "b",
3087 ParameterValue::Integer(1),
3088 Some(bad_range)
3089 ),
3090 Err(DeclarationError::InvalidRange)
3091 );
3092
3093 let out_of_range = ParameterDescriptor::new("b", ParameterType::Integer)
3094 .expect("name fits")
3095 .with_integer_range(0, 10, 1);
3096 assert_eq!(
3097 server.try_declare_with_descriptor(
3098 NODE,
3099 "b",
3100 ParameterValue::Integer(99),
3101 Some(out_of_range)
3102 ),
3103 Err(DeclarationError::InitialValueOutOfRange)
3104 );
3105
3106 assert_eq!(
3107 server.try_declare(NODE, "b", ParameterValue::Integer(1)),
3108 Ok(())
3109 );
3110 assert_eq!(
3111 server.try_declare(NODE, "c", ParameterValue::Integer(1)),
3112 Err(DeclarationError::StorageFull)
3113 );
3114
3115 let long = "n".repeat(MAX_PARAM_NAME_LEN + 1);
3116 let mut roomy: ParameterStorage<2> = ParameterStorage::new();
3117 let mut server = ParameterServer::new_in(roomy.as_table());
3118 assert_eq!(
3119 server.try_declare(NODE, &long, ParameterValue::Integer(1)),
3120 Err(DeclarationError::NameTooLong)
3121 );
3122 }
3123}
3124
3125#[cfg(test)]
3130mod ghost_checks {
3131 use super::*;
3132 use nros_ghost_types::ParamServerGhost;
3133
3134 const NODE: NodeKey = NodeKey::PRIMARY;
3136
3137 fn ghost_from_server(s: &ParameterServer<'_>) -> ParamServerGhost {
3140 ParamServerGhost {
3141 count: s.count,
3142 max: s.table.capacity(),
3146 }
3147 }
3148
3149 #[test]
3150 fn ghost_new_count() {
3151 let mut storage: ParameterStorage = ParameterStorage::new();
3152 let capacity = storage.capacity();
3153 let server = ParameterServer::new_in(storage.as_table());
3154 let ghost = ghost_from_server(&server);
3155 assert_eq!(ghost.count, 0);
3156 assert_eq!(ghost.max, capacity);
3161 assert_eq!(ghost.max, MAX_PARAMETERS);
3162 }
3163
3164 #[test]
3167 fn ghost_max_follows_a_shorter_table() {
3168 let mut storage = ParameterStorage::<2>::new();
3169 let mut server = ParameterServer::new_in(storage.as_table());
3170 assert_eq!(ghost_from_server(&server).max, 2);
3171 assert!(server.declare(NODE, "a", ParameterValue::Integer(1)));
3172 assert!(server.declare(NODE, "b", ParameterValue::Integer(2)));
3173 assert!(server.is_full());
3174 assert!(!server.declare(NODE, "c", ParameterValue::Integer(3)));
3175 let ghost = ghost_from_server(&server);
3176 assert!(ghost.count <= ghost.max);
3177 }
3178
3179 #[test]
3183 fn ghost_count_recovers_from_a_reused_table() {
3184 let mut storage = ParameterStorage::<4>::new();
3185 {
3186 let mut first = ParameterServer::new_in(storage.as_table());
3187 assert!(first.declare(NODE, "kept", ParameterValue::Integer(7)));
3188 }
3189 let second = ParameterServer::new_in(storage.as_table());
3190 assert_eq!(ghost_from_server(&second).count, 1);
3191 assert_eq!(second.get_integer(NODE, "kept"), Some(7));
3192 }
3193
3194 #[test]
3195 fn ghost_declare_increments() {
3196 let mut storage: ParameterStorage = ParameterStorage::new();
3197 let mut server = ParameterServer::new_in(storage.as_table());
3198 let before = ghost_from_server(&server).count;
3199 assert!(server.declare(NODE, "test", ParameterValue::Integer(1)));
3200 let after = ghost_from_server(&server).count;
3201 assert_eq!(after, before + 1);
3202 }
3203
3204 #[test]
3205 fn ghost_remove_decrements() {
3206 let mut storage: ParameterStorage = ParameterStorage::new();
3207 let mut server = ParameterServer::new_in(storage.as_table());
3208 assert!(server.declare(NODE, "test", ParameterValue::Integer(1)));
3209 let before = ghost_from_server(&server).count;
3210 assert!(server.remove(NODE, "test"));
3211 let after = ghost_from_server(&server).count;
3212 assert_eq!(after, before - 1);
3213 }
3214
3215 #[test]
3216 fn ghost_count_bounded() {
3217 let mut storage: ParameterStorage = ParameterStorage::new();
3218 let mut server = ParameterServer::new_in(storage.as_table());
3219 for i in 0..MAX_PARAMETERS {
3220 let mut name = heapless::String::<64>::new();
3221 let _ = core::fmt::write(&mut name, format_args!("p{}", i));
3222 assert!(server.declare(NODE, name.as_str(), ParameterValue::Integer(i as i64)));
3223 }
3224 let ghost = ghost_from_server(&server);
3225 assert!(ghost.count <= ghost.max);
3226 assert_eq!(ghost.count, MAX_PARAMETERS);
3227 assert!(!server.declare(NODE, "overflow", ParameterValue::Integer(0)));
3229 let ghost2 = ghost_from_server(&server);
3230 assert!(ghost2.count <= ghost2.max);
3231 }
3232}
3233
3234#[cfg(kani)]
3239mod verification {
3240 use super::*;
3241
3242 const NODE: NodeKey = NodeKey::PRIMARY;
3244
3245 #[kani::proof]
3251 fn server_new_is_empty() {
3252 let mut storage: ParameterStorage = ParameterStorage::new();
3253 let server = ParameterServer::new_in(storage.as_table());
3254 assert!(server.is_empty(NODE));
3255 assert_eq!(server.len(NODE), 0);
3256 assert!(!server.is_full());
3257 }
3258
3259 #[kani::proof]
3260 fn server_declare_get_roundtrip_integer() {
3261 let mut storage: ParameterStorage = ParameterStorage::new();
3262 let mut server = ParameterServer::new_in(storage.as_table());
3263 let val: i64 = kani::any();
3264 assert!(server.declare(NODE, "test", ParameterValue::Integer(val)));
3265 assert_eq!(server.get_integer(NODE, "test"), Some(val));
3266 assert!(server.has(NODE, "test"));
3267 assert_eq!(server.len(NODE), 1);
3268 }
3269
3270 #[kani::proof]
3271 fn server_declare_get_roundtrip_bool() {
3272 let mut storage: ParameterStorage = ParameterStorage::new();
3273 let mut server = ParameterServer::new_in(storage.as_table());
3274 let val: bool = kani::any();
3275 assert!(server.declare(NODE, "test", ParameterValue::Bool(val)));
3276 assert_eq!(server.get_bool(NODE, "test"), Some(val));
3277 }
3278
3279 #[kani::proof]
3280 fn server_set_requires_declare() {
3281 let mut storage: ParameterStorage = ParameterStorage::new();
3282 let mut server = ParameterServer::new_in(storage.as_table());
3283 let val: i64 = kani::any();
3284 let result = server.set(NODE, "test", ParameterValue::Integer(val));
3286 assert_eq!(result, Err(ParameterError::NotFound));
3287 }
3288
3289 #[kani::proof]
3290 fn server_duplicate_declare_fails() {
3291 let mut storage: ParameterStorage = ParameterStorage::new();
3292 let mut server = ParameterServer::new_in(storage.as_table());
3293 assert!(server.declare(NODE, "test", ParameterValue::Integer(1)));
3294 assert!(!server.declare(NODE, "test", ParameterValue::Integer(2)));
3295 assert_eq!(server.get_integer(NODE, "test"), Some(1));
3297 }
3298
3299 #[kani::proof]
3300 fn server_remove_clears() {
3301 let mut storage: ParameterStorage = ParameterStorage::new();
3302 let mut server = ParameterServer::new_in(storage.as_table());
3303 assert!(server.declare(NODE, "test", ParameterValue::Integer(42)));
3304 assert!(server.has(NODE, "test"));
3305 assert!(server.remove(NODE, "test"));
3306 assert!(!server.has(NODE, "test"));
3307 assert_eq!(server.len(NODE), 0);
3308 }
3309
3310 #[kani::proof]
3311 fn server_get_nonexistent_returns_none() {
3312 let mut storage: ParameterStorage = ParameterStorage::new();
3313 let server = ParameterServer::new_in(storage.as_table());
3314 assert!(server.get(NODE, "nonexistent").is_none());
3315 assert!(server.get_bool(NODE, "nonexistent").is_none());
3316 assert!(server.get_integer(NODE, "nonexistent").is_none());
3317 assert!(server.get_double(NODE, "nonexistent").is_none());
3318 assert!(server.get_string(NODE, "nonexistent").is_none());
3319 }
3320}