1use crate::types::{
7 FloatingPointRange, IntegerRange, MAX_PARAM_NAME_LEN, NodeFlags, NodeKey, Parameter,
8 ParameterDescriptor, ParameterRange, ParameterType, ParameterValue, SetParameterResult,
9};
10use heapless::String;
11
12use crate::types::MAX_PARAMETERS;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ParameterEventKind {
20 New,
22 Changed,
24 Deleted,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct PendingParameterEvent {
31 pub node: NodeKey,
33 pub kind: ParameterEventKind,
35 pub name: String<MAX_PARAM_NAME_LEN>,
37}
38
39pub const PARAMETER_EVENT_LOG_LEN: usize = 8;
48
49#[derive(Debug, Clone)]
55pub(crate) struct ParameterEntry {
56 node: NodeKey,
60 param: Parameter,
62 descriptor: Option<ParameterDescriptor>,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum DeclarationError {
80 AlreadyDeclared,
82 InvalidRange,
85 InitialValueOutOfRange,
88 StorageFull,
91 NameTooLong,
94}
95
96pub type OnSetParameterFn = fn(
114 node: NodeKey,
115 name: &str,
116 old: Option<&ParameterValue>,
117 new: Option<&ParameterValue>,
118 ctx: OnSetContext,
119) -> bool;
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
135pub struct OnSetContext {
136 pub a: usize,
139 pub b: usize,
141}
142
143impl OnSetContext {
144 pub const fn new(a: usize) -> Self {
146 Self { a, b: 0 }
147 }
148
149 pub const fn pair(a: usize, b: usize) -> Self {
151 Self { a, b }
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct OnSetParameterHandle(u16);
164
165impl OnSetParameterHandle {
166 pub const fn raw(self) -> u16 {
173 self.0
174 }
175
176 pub const fn from_raw(raw: u16) -> Self {
178 Self(raw)
179 }
180}
181
182#[derive(Clone, Copy)]
183struct OnSetCallbackSlot {
184 node: NodeKey,
185 callback: OnSetParameterFn,
186 ctx: OnSetContext,
187}
188
189pub const MAX_ON_SET_CALLBACKS: usize = 4;
205
206pub struct ParameterTable<'s> {
222 entries: &'s mut [Option<ParameterEntry>],
223}
224
225impl<'s> ParameterTable<'s> {
226 pub(crate) fn new(entries: &'s mut [Option<ParameterEntry>]) -> Self {
228 Self { entries }
229 }
230
231 pub fn capacity(&self) -> usize {
233 self.entries.len()
234 }
235
236 pub fn occupied(&self) -> usize {
238 self.entries.iter().filter(|slot| slot.is_some()).count()
239 }
240}
241
242#[repr(transparent)]
254pub struct ParameterSlot(Option<ParameterEntry>);
255
256impl<'s> ParameterTable<'s> {
257 pub fn init_in(slots: &'s mut [core::mem::MaybeUninit<ParameterSlot>]) -> Self {
265 for slot in slots.iter_mut() {
266 slot.write(ParameterSlot(None));
267 }
268 let len = slots.len();
269 let base = slots.as_mut_ptr().cast::<Option<ParameterEntry>>();
270 let entries = unsafe { core::slice::from_raw_parts_mut(base, len) };
275 Self::new(entries)
276 }
277}
278
279impl core::fmt::Debug for ParameterTable<'_> {
280 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
281 f.debug_struct("ParameterTable")
282 .field("capacity", &self.capacity())
283 .finish_non_exhaustive()
284 }
285}
286
287pub struct ParameterStorage<const N: usize = MAX_PARAMETERS> {
339 entries: [Option<ParameterEntry>; N],
340}
341
342impl<const N: usize> ParameterStorage<N> {
343 #[allow(clippy::new_without_default)]
352 #[allow(clippy::large_stack_arrays)] pub const fn new() -> Self {
354 Self {
355 entries: [const { None }; N],
356 }
357 }
358
359 pub const fn capacity(&self) -> usize {
361 N
362 }
363
364 pub fn as_table(&mut self) -> ParameterTable<'_> {
366 ParameterTable::new(&mut self.entries)
367 }
368
369 pub unsafe fn init_in_place(dst: *mut Self) {
386 unsafe {
390 let entries = core::ptr::addr_of_mut!((*dst).entries).cast::<Option<ParameterEntry>>();
391 for i in 0..N {
392 entries.add(i).write(None);
393 }
394 }
395 }
396}
397
398pub struct ParameterServer<'s> {
440 table: ParameterTable<'s>,
442 count: usize,
444 allow_undeclared: NodeFlags,
452 delete_on_not_set: NodeFlags,
458 truncation_pending: bool,
463 events_enabled: NodeFlags,
465 event_log: heapless::Deque<PendingParameterEvent, PARAMETER_EVENT_LOG_LEN>,
467 events_dropped: u32,
469 on_set: [Option<OnSetCallbackSlot>; MAX_ON_SET_CALLBACKS],
472}
473
474impl<'s> ParameterServer<'s> {
475 pub fn take_truncated_descriptions(&mut self, mut report: impl FnMut(NodeKey, &str)) -> usize {
485 if !self.truncation_pending {
486 return 0;
487 }
488 self.truncation_pending = false;
489 let mut n = 0;
490 for entry in self.table.entries.iter_mut().flatten() {
491 if let Some(d) = entry.descriptor.as_mut()
492 && d.description_truncated()
493 {
494 d.clear_description_truncated();
495 report(entry.node, entry.param.name.as_str());
496 n += 1;
497 }
498 }
499 n
500 }
501
502 #[inline]
504 fn note_descriptor(&mut self, descriptor: Option<&ParameterDescriptor>) {
505 if descriptor.is_some_and(ParameterDescriptor::description_truncated) {
506 self.truncation_pending = true;
507 }
508 }
509
510 pub fn new_in(table: ParameterTable<'s>) -> Self {
521 let count = table.occupied();
522 Self {
523 table,
524 count,
525 allow_undeclared: NodeFlags::new(),
526 delete_on_not_set: NodeFlags::new(),
527 on_set: [None; MAX_ON_SET_CALLBACKS],
528 truncation_pending: true,
531 events_enabled: NodeFlags::new(),
532 event_log: heapless::Deque::new(),
533 events_dropped: 0,
534 }
535 }
536
537 pub fn set_allow_undeclared(&mut self, node: NodeKey, allow: bool) {
546 self.allow_undeclared.set(node, allow);
547 }
548
549 pub fn allows_undeclared(&self, node: NodeKey) -> bool {
551 self.allow_undeclared.get(node)
552 }
553
554 pub fn set_parameter_events(&mut self, node: NodeKey, on: bool) {
558 self.events_enabled.set(node, on);
559 }
560
561 pub fn pop_parameter_event(&mut self) -> Option<PendingParameterEvent> {
563 self.event_log.pop_front()
564 }
565
566 pub fn take_parameter_events_dropped(&mut self) -> u32 {
569 core::mem::take(&mut self.events_dropped)
570 }
571
572 fn note_event(&mut self, node: NodeKey, kind: ParameterEventKind, name: &str) {
575 if !self.events_enabled.get(node) {
576 return;
577 }
578 if let Some(e) = self
579 .event_log
580 .iter_mut()
581 .find(|e| e.node == node && e.name.as_str() == name)
582 {
583 e.kind = match (e.kind, kind) {
586 (ParameterEventKind::New, ParameterEventKind::Changed) => ParameterEventKind::New,
587 (_, k) => k,
588 };
589 return;
590 }
591 let mut stored = String::new();
592 if stored.push_str(name).is_err()
593 || self
594 .event_log
595 .push_back(PendingParameterEvent {
596 node,
597 kind,
598 name: stored,
599 })
600 .is_err()
601 {
602 self.events_dropped = self.events_dropped.saturating_add(1);
603 }
604 }
605
606 pub fn set_delete_on_not_set(&mut self, node: NodeKey, on: bool) {
611 self.delete_on_not_set.set(node, on);
612 }
613
614 pub fn len(&self, node: NodeKey) -> usize {
616 self.entries_of(node).count()
617 }
618
619 pub fn is_empty(&self, node: NodeKey) -> bool {
621 self.len(node) == 0
622 }
623
624 pub fn total_len(&self) -> usize {
628 self.count
629 }
630
631 pub fn capacity(&self) -> usize {
639 self.table.capacity()
640 }
641
642 pub fn is_full(&self) -> bool {
644 self.count >= self.table.capacity()
645 }
646
647 fn entries_of(&self, node: NodeKey) -> impl Iterator<Item = &ParameterEntry> {
649 self.table
650 .entries
651 .iter()
652 .filter_map(|slot| slot.as_ref())
653 .filter(move |e| e.node == node)
654 }
655
656 fn find_index(&self, node: NodeKey, name: &str) -> Option<usize> {
661 self.table.entries.iter().position(|entry| {
662 entry
663 .as_ref()
664 .map(|e| e.node == node && e.param.name.as_str() == name)
665 .unwrap_or(false)
666 })
667 }
668
669 fn find_empty_slot(&self) -> Option<usize> {
671 self.table.entries.iter().position(|entry| entry.is_none())
672 }
673
674 #[must_use]
683 pub fn declare(&mut self, node: NodeKey, name: &str, value: ParameterValue) -> bool {
684 self.declare_with_descriptor(node, name, value, None)
685 }
686
687 #[must_use]
694 pub fn declare_with_descriptor(
695 &mut self,
696 node: NodeKey,
697 name: &str,
698 value: ParameterValue,
699 descriptor: Option<ParameterDescriptor>,
700 ) -> bool {
701 self.try_declare_with_descriptor(node, name, value, descriptor)
702 .is_ok()
703 }
704
705 pub fn try_declare(
714 &mut self,
715 node: NodeKey,
716 name: &str,
717 value: ParameterValue,
718 ) -> Result<(), DeclarationError> {
719 self.try_declare_with_descriptor(node, name, value, None)
720 }
721
722 pub fn try_declare_with_descriptor(
725 &mut self,
726 node: NodeKey,
727 name: &str,
728 value: ParameterValue,
729 descriptor: Option<ParameterDescriptor>,
730 ) -> Result<(), DeclarationError> {
731 if self.find_index(node, name).is_some() {
733 return Err(DeclarationError::AlreadyDeclared);
734 }
735
736 Self::declaration_consistency(descriptor.as_ref(), &value)?;
739
740 let slot = match self.find_empty_slot() {
742 Some(idx) => idx,
743 None => return Err(DeclarationError::StorageFull),
744 };
745
746 let param = match Parameter::new(name, value) {
748 Some(p) => p,
749 None => return Err(DeclarationError::NameTooLong),
750 };
751
752 self.note_descriptor(descriptor.as_ref());
753 self.table.entries[slot] = Some(ParameterEntry {
754 node,
755 param,
756 descriptor,
757 });
758 self.count += 1;
759 self.note_event(node, ParameterEventKind::New, name);
760 Ok(())
761 }
762
763 fn declaration_consistency(
773 descriptor: Option<&ParameterDescriptor>,
774 value: &ParameterValue,
775 ) -> Result<(), DeclarationError> {
776 match descriptor {
777 Some(desc) if !desc.range.is_valid() => Err(DeclarationError::InvalidRange),
778 Some(desc) if !desc.validate_range(value) => {
779 Err(DeclarationError::InitialValueOutOfRange)
780 }
781 _ => Ok(()),
782 }
783 }
784
785 fn declaration_is_consistent(
786 descriptor: Option<&ParameterDescriptor>,
787 value: &ParameterValue,
788 ) -> bool {
789 Self::declaration_consistency(descriptor, value).is_ok()
790 }
791
792 fn check_set_existing(entry: &ParameterEntry, value: &ParameterValue) -> SetParameterResult {
795 if let Some(ref desc) = entry.descriptor {
796 if desc.read_only {
797 return SetParameterResult::ReadOnly;
798 }
799 if !desc.dynamic_typing && desc.param_type != value.param_type() {
800 return SetParameterResult::TypeMismatch;
801 }
802 if !desc.validate_range(value) {
803 return SetParameterResult::OutOfRange;
804 }
805 }
806 SetParameterResult::Success
807 }
808
809 pub fn check_apply(
813 &self,
814 node: NodeKey,
815 name: &str,
816 value: &ParameterValue,
817 ) -> SetParameterResult {
818 match self
819 .find_index(node, name)
820 .and_then(|idx| self.table.entries[idx].as_ref())
821 {
822 Some(entry) => Self::check_set_existing(entry, value),
823 None if !self.allows_undeclared(node) => SetParameterResult::Undeclared,
824 None if self.is_full() => SetParameterResult::StorageFull,
825 None if name.len() > MAX_PARAM_NAME_LEN => SetParameterResult::StorageFull,
826 None => SetParameterResult::Success,
827 }
828 }
829
830 pub fn apply(
841 &mut self,
842 node: NodeKey,
843 name: &str,
844 value: ParameterValue,
845 ) -> SetParameterResult {
846 if matches!(value, ParameterValue::NotSet) && self.delete_on_not_set.get(node) {
847 return self.apply_delete(node, name);
848 }
849 let verdict = self.check_apply(node, name, &value);
850 if !verdict.is_success() {
851 return verdict;
852 }
853 if !self.run_on_set_callbacks(node, name, self.get(node, name), Some(&value)) {
861 return SetParameterResult::Rejected;
862 }
863 if self.find_index(node, name).is_some() {
864 self.set(node, name, value)
865 } else if self.declare(node, name, value) {
866 SetParameterResult::Success
867 } else {
868 SetParameterResult::StorageFull
869 }
870 }
871
872 pub fn apply_staged(
889 &mut self,
890 node: NodeKey,
891 name: &str,
892 staged: &mut ParameterValue,
893 ) -> SetParameterResult {
894 if matches!(staged, ParameterValue::NotSet) && self.delete_on_not_set.get(node) {
895 return self.apply_delete(node, name);
896 }
897 let verdict = self.check_apply(node, name, staged);
898 if !verdict.is_success() {
899 return verdict;
900 }
901 if !self.run_on_set_callbacks(node, name, self.get(node, name), Some(staged)) {
902 return SetParameterResult::Rejected;
903 }
904 let (idx, kind) = match self.find_index(node, name) {
905 Some(idx) => (idx, ParameterEventKind::Changed),
906 None => match self.declare_placeholder(node, name) {
907 Some(idx) => (idx, ParameterEventKind::New),
908 None => return SetParameterResult::StorageFull,
909 },
910 };
911 match self.table.entries[idx].as_mut() {
912 Some(entry) => {
913 core::mem::swap(&mut entry.param.value, staged);
914 self.note_event(node, kind, name);
915 SetParameterResult::Success
916 }
917 None => SetParameterResult::NotFound,
918 }
919 }
920
921 #[inline(never)]
928 fn declare_placeholder(&mut self, node: NodeKey, name: &str) -> Option<usize> {
929 let slot = self.find_empty_slot()?;
930 let mut stored = String::new();
931 stored.push_str(name).ok()?;
932 self.table.entries[slot] = Some(ParameterEntry {
933 node,
934 param: Parameter {
935 name: stored,
936 value: ParameterValue::NotSet,
937 },
938 descriptor: None,
939 });
940 self.count += 1;
941 Some(slot)
942 }
943
944 fn apply_delete(&mut self, node: NodeKey, name: &str) -> SetParameterResult {
948 let Some(entry) = self
949 .find_index(node, name)
950 .and_then(|idx| self.table.entries[idx].as_ref())
951 else {
952 return SetParameterResult::NotFound;
953 };
954 if entry.descriptor.as_ref().is_some_and(|d| d.read_only) {
955 return SetParameterResult::ReadOnly;
956 }
957 if !self.run_on_set_callbacks(node, name, self.get(node, name), None) {
958 return SetParameterResult::Rejected;
959 }
960 self.remove(node, name);
961 SetParameterResult::Success
962 }
963
964 pub fn set_local(
978 &mut self,
979 node: NodeKey,
980 name: &str,
981 value: ParameterValue,
982 ) -> SetParameterResult {
983 let Some(idx) = self.find_index(node, name) else {
984 return SetParameterResult::NotFound;
985 };
986 let Some(entry) = self.table.entries[idx].as_ref() else {
987 return SetParameterResult::NotFound;
988 };
989 let dynamic = entry.descriptor.as_ref().is_some_and(|d| d.dynamic_typing);
990 if !dynamic && entry.param.value.param_type() != value.param_type() {
991 return SetParameterResult::TypeMismatch;
992 }
993 if !self.run_on_set_callbacks(node, name, Some(&entry.param.value), Some(&value)) {
994 return SetParameterResult::Rejected;
995 }
996 if let Some(entry) = self.table.entries[idx].as_mut() {
997 entry.param.value = value;
998 }
999 self.note_event(node, ParameterEventKind::Changed, name);
1000 SetParameterResult::Success
1001 }
1002
1003 fn run_on_set_callbacks(
1007 &self,
1008 node: NodeKey,
1009 name: &str,
1010 old: Option<&ParameterValue>,
1011 new: Option<&ParameterValue>,
1012 ) -> bool {
1013 for slot in self.on_set.iter().flatten() {
1014 if slot.node == node && !(slot.callback)(node, name, old, new, slot.ctx) {
1015 return false;
1016 }
1017 }
1018 true
1019 }
1020
1021 pub fn add_on_set_parameters_callback(
1033 &mut self,
1034 node: NodeKey,
1035 callback: OnSetParameterFn,
1036 ctx: OnSetContext,
1037 ) -> Option<OnSetParameterHandle> {
1038 let idx = self.on_set.iter().position(Option::is_none)?;
1039 self.on_set[idx] = Some(OnSetCallbackSlot {
1040 node,
1041 callback,
1042 ctx,
1043 });
1044 Some(OnSetParameterHandle(idx as u16))
1045 }
1046
1047 pub fn remove_on_set_parameters_callback(&mut self, handle: OnSetParameterHandle) -> bool {
1050 let idx = handle.0 as usize;
1051 if idx >= MAX_ON_SET_CALLBACKS || self.on_set[idx].is_none() {
1052 return false;
1053 }
1054 self.on_set[idx] = None;
1055 true
1056 }
1057
1058 pub fn on_set_callbacks_free(&self) -> usize {
1061 self.on_set.iter().filter(|s| s.is_none()).count()
1062 }
1063
1064 fn descriptor_mut(&mut self, node: NodeKey, name: &str) -> Option<&mut ParameterDescriptor> {
1081 let idx = self.find_index(node, name)?;
1082 let entry = self.table.entries[idx].as_mut()?;
1083 if entry.descriptor.is_none() {
1084 entry.descriptor = Some(ParameterDescriptor::new(
1085 name,
1086 entry.param.value.param_type(),
1087 )?);
1088 }
1089 entry.descriptor.as_mut()
1090 }
1091
1092 pub fn set_parameter_description(
1096 &mut self,
1097 node: NodeKey,
1098 name: &str,
1099 description: &str,
1100 additional_constraints: &str,
1101 ) -> bool {
1102 let mut truncated = false;
1103 let found = match self.descriptor_mut(node, name) {
1104 Some(desc) => {
1105 truncated |= desc.set_description(description);
1106 truncated |= desc.set_additional_constraints(additional_constraints);
1107 true
1108 }
1109 None => false,
1110 };
1111 self.truncation_pending |= truncated;
1114 found
1115 }
1116
1117 pub fn set_parameter_read_only(&mut self, node: NodeKey, name: &str, read_only: bool) -> bool {
1121 match self.descriptor_mut(node, name) {
1122 Some(desc) => {
1123 desc.read_only = read_only;
1124 true
1125 }
1126 None => false,
1127 }
1128 }
1129
1130 pub fn set_parameter_integer_range(
1138 &mut self,
1139 node: NodeKey,
1140 name: &str,
1141 min: i64,
1142 max: i64,
1143 step: i64,
1144 ) -> bool {
1145 self.set_parameter_range(
1146 node,
1147 name,
1148 ParameterRange::Integer(IntegerRange::new(min, max, step)),
1149 )
1150 }
1151
1152 pub fn set_parameter_float_range(
1156 &mut self,
1157 node: NodeKey,
1158 name: &str,
1159 min: f64,
1160 max: f64,
1161 step: f64,
1162 ) -> bool {
1163 self.set_parameter_range(
1164 node,
1165 name,
1166 ParameterRange::FloatingPoint(FloatingPointRange::new(min, max, step)),
1167 )
1168 }
1169
1170 fn set_parameter_range(&mut self, node: NodeKey, name: &str, range: ParameterRange) -> bool {
1172 if !range.is_valid() {
1173 return false;
1174 }
1175 let idx = match self.find_index(node, name) {
1176 Some(idx) => idx,
1177 None => return false,
1178 };
1179 let value = match self.table.entries[idx].as_ref() {
1183 Some(entry) => entry.param.value.clone(),
1184 None => return false,
1185 };
1186 if !range.contains(&value) {
1187 return false;
1188 }
1189 match self.descriptor_mut(node, name) {
1190 Some(desc) => {
1191 desc.range = range;
1192 true
1193 }
1194 None => false,
1195 }
1196 }
1197
1198 pub fn apply_atomically<'x, I>(&mut self, node: NodeKey, items: I) -> SetParameterResult
1224 where
1225 I: Iterator<Item = Option<(&'x str, ParameterValue)>> + Clone,
1226 {
1227 for item in items.clone() {
1228 let Some((name, value)) = item else {
1229 return SetParameterResult::TypeMismatch;
1230 };
1231 let verdict = self.check_apply(node, name, &value);
1232 if !verdict.is_success() {
1233 return verdict;
1234 }
1235 if !self.run_on_set_callbacks(node, name, self.get(node, name), Some(&value)) {
1241 return SetParameterResult::Rejected;
1242 }
1243 }
1244 for item in items {
1245 let Some((name, value)) = item else {
1246 continue;
1247 };
1248 let _ = self.apply(node, name, value);
1249 }
1250 SetParameterResult::Success
1251 }
1252
1253 pub fn get(&self, node: NodeKey, name: &str) -> Option<&ParameterValue> {
1255 self.find_index(node, name)
1256 .and_then(|idx| self.table.entries[idx].as_ref())
1257 .map(|entry| &entry.param.value)
1258 }
1259
1260 pub fn get_parameter(&self, node: NodeKey, name: &str) -> Option<&Parameter> {
1262 self.find_index(node, name)
1263 .and_then(|idx| self.table.entries[idx].as_ref())
1264 .map(|entry| &entry.param)
1265 }
1266
1267 pub fn get_descriptor(&self, node: NodeKey, name: &str) -> Option<&ParameterDescriptor> {
1269 self.find_index(node, name)
1270 .and_then(|idx| self.table.entries[idx].as_ref())
1271 .and_then(|entry| entry.descriptor.as_ref())
1272 }
1273
1274 pub fn set(&mut self, node: NodeKey, name: &str, value: ParameterValue) -> SetParameterResult {
1278 let idx = match self.find_index(node, name) {
1279 Some(idx) => idx,
1280 None => return SetParameterResult::NotFound,
1281 };
1282
1283 let entry = match self.table.entries[idx].as_mut() {
1284 Some(e) => e,
1285 None => return SetParameterResult::NotFound,
1286 };
1287
1288 let verdict = Self::check_set_existing(entry, &value);
1291 if !verdict.is_success() {
1292 return verdict;
1293 }
1294
1295 entry.param.value = value;
1296 self.note_event(node, ParameterEventKind::Changed, name);
1297 SetParameterResult::Success
1298 }
1299
1300 pub fn unset(&mut self, node: NodeKey, name: &str) -> SetParameterResult {
1305 let idx = match self.find_index(node, name) {
1306 Some(idx) => idx,
1307 None => return SetParameterResult::NotFound,
1308 };
1309
1310 let entry = match self.table.entries[idx].as_mut() {
1311 Some(e) => e,
1312 None => return SetParameterResult::NotFound,
1313 };
1314
1315 if let Some(ref desc) = entry.descriptor
1317 && desc.read_only
1318 {
1319 return SetParameterResult::ReadOnly;
1320 }
1321
1322 entry.param.value = ParameterValue::NotSet;
1323 self.note_event(node, ParameterEventKind::Changed, name);
1324 SetParameterResult::Success
1325 }
1326
1327 pub fn set_or_declare(
1336 &mut self,
1337 node: NodeKey,
1338 name: &str,
1339 value: ParameterValue,
1340 ) -> SetParameterResult {
1341 if self.find_index(node, name).is_some() {
1342 self.set(node, name, value)
1343 } else if self.declare(node, name, value) {
1344 SetParameterResult::Success
1345 } else {
1346 SetParameterResult::StorageFull
1347 }
1348 }
1349
1350 pub fn has(&self, node: NodeKey, name: &str) -> bool {
1352 self.find_index(node, name).is_some()
1353 }
1354
1355 pub fn remove(&mut self, node: NodeKey, name: &str) -> bool {
1359 if let Some(idx) = self.find_index(node, name) {
1360 self.table.entries[idx] = None;
1361 self.count -= 1;
1362 self.note_event(node, ParameterEventKind::Deleted, name);
1363 true
1364 } else {
1365 false
1366 }
1367 }
1368
1369 pub fn get_type(&self, node: NodeKey, name: &str) -> Option<ParameterType> {
1371 self.get(node, name).map(|v| v.param_type())
1372 }
1373
1374 pub fn iter(&self, node: NodeKey) -> impl Iterator<Item = &Parameter> {
1376 self.entries_of(node).map(|e| &e.param)
1377 }
1378
1379 pub fn list_names(&self, node: NodeKey) -> impl Iterator<Item = &str> {
1381 self.iter(node).map(|p| p.name.as_str())
1382 }
1383
1384 pub fn list_with_prefix<'a>(
1386 &'a self,
1387 node: NodeKey,
1388 prefix: &'a str,
1389 ) -> impl Iterator<Item = &'a str> {
1390 self.list_names(node)
1391 .filter(move |name| name.starts_with(prefix))
1392 }
1393
1394 pub fn get_bool(&self, node: NodeKey, name: &str) -> Option<bool> {
1396 self.get(node, name).and_then(|v| v.as_bool())
1397 }
1398
1399 pub fn get_integer(&self, node: NodeKey, name: &str) -> Option<i64> {
1401 self.get(node, name).and_then(|v| v.as_integer())
1402 }
1403
1404 pub fn get_double(&self, node: NodeKey, name: &str) -> Option<f64> {
1406 self.get(node, name).and_then(|v| v.as_double())
1407 }
1408
1409 pub fn get_string(&self, node: NodeKey, name: &str) -> Option<&str> {
1411 self.get(node, name).and_then(|v| v.as_string())
1412 }
1413
1414 pub fn set_bool(&mut self, node: NodeKey, name: &str, value: bool) -> SetParameterResult {
1416 self.set(node, name, ParameterValue::Bool(value))
1417 }
1418
1419 pub fn set_integer(&mut self, node: NodeKey, name: &str, value: i64) -> SetParameterResult {
1421 self.set(node, name, ParameterValue::Integer(value))
1422 }
1423
1424 pub fn set_double(&mut self, node: NodeKey, name: &str, value: f64) -> SetParameterResult {
1426 self.set(node, name, ParameterValue::Double(value))
1427 }
1428
1429 pub fn set_string(&mut self, node: NodeKey, name: &str, value: &str) -> SetParameterResult {
1431 match ParameterValue::from_string(value) {
1432 Some(v) => self.set(node, name, v),
1433 None => SetParameterResult::StorageFull, }
1435 }
1436
1437 pub fn declare_parameter(
1447 &mut self,
1448 node: NodeKey,
1449 descriptor: ParameterDescriptor,
1450 initial_value: Option<&ParameterValue>,
1451 ) -> Result<(), SetParameterResult> {
1452 let name = descriptor.name.as_str();
1453
1454 if self.find_index(node, name).is_some() {
1456 return Err(SetParameterResult::TypeMismatch); }
1458
1459 let param_value = initial_value.cloned().unwrap_or_default();
1460
1461 if !Self::declaration_is_consistent(Some(&descriptor), ¶m_value) {
1463 return Err(SetParameterResult::InvalidRange);
1464 }
1465
1466 let slot = match self.find_empty_slot() {
1468 Some(idx) => idx,
1469 None => return Err(SetParameterResult::StorageFull),
1470 };
1471
1472 let param = Parameter::new(name, param_value).ok_or(SetParameterResult::StorageFull)?; self.note_descriptor(Some(&descriptor));
1475 self.table.entries[slot] = Some(ParameterEntry {
1476 node,
1477 param,
1478 descriptor: Some(descriptor),
1479 });
1480 self.count += 1;
1481 Ok(())
1482 }
1483
1484 pub fn get_parameter_value(&self, node: NodeKey, name: &str) -> Option<ParameterValue> {
1489 self.get(node, name).cloned()
1490 }
1491
1492 pub fn set_parameter_value(
1497 &mut self,
1498 node: NodeKey,
1499 name: &str,
1500 value: ParameterValue,
1501 ) -> SetParameterResult {
1502 self.set(node, name, value)
1503 }
1504}
1505
1506pub struct LegacyParameterBuilder<'a, 's> {
1508 server: &'a mut ParameterServer<'s>,
1509 node: NodeKey,
1512 name: String<MAX_PARAM_NAME_LEN>,
1513 value: ParameterValue,
1514 descriptor: Option<ParameterDescriptor>,
1515}
1516
1517impl<'a, 's> LegacyParameterBuilder<'a, 's> {
1518 pub fn new(
1520 server: &'a mut ParameterServer<'s>,
1521 node: NodeKey,
1522 name: &str,
1523 value: ParameterValue,
1524 ) -> Option<Self> {
1525 let mut n = String::new();
1526 n.push_str(name).ok()?;
1527 Some(Self {
1528 server,
1529 node,
1530 name: n,
1531 value,
1532 descriptor: None,
1533 })
1534 }
1535
1536 pub fn description(mut self, desc: &str) -> Self {
1538 let param_type = self.value.param_type();
1539 if self.descriptor.is_none() {
1540 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1541 }
1542 if let Some(ref mut d) = self.descriptor {
1545 d.set_description(desc);
1546 }
1547 self
1548 }
1549
1550 pub fn read_only(mut self) -> Self {
1552 let param_type = self.value.param_type();
1553 if self.descriptor.is_none() {
1554 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1555 }
1556 if let Some(ref mut d) = self.descriptor {
1557 d.read_only = true;
1558 }
1559 self
1560 }
1561
1562 pub fn integer_range(mut self, min: i64, max: i64, step: i64) -> Self {
1564 let param_type = self.value.param_type();
1565 if self.descriptor.is_none() {
1566 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1567 }
1568 if let Some(ref mut d) = self.descriptor {
1569 d.range = crate::types::ParameterRange::Integer(crate::types::IntegerRange::new(
1570 min, max, step,
1571 ));
1572 }
1573 self
1574 }
1575
1576 pub fn float_range(mut self, min: f64, max: f64, step: f64) -> Self {
1578 let param_type = self.value.param_type();
1579 if self.descriptor.is_none() {
1580 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
1581 }
1582 if let Some(ref mut d) = self.descriptor {
1583 d.range = crate::types::ParameterRange::FloatingPoint(
1584 crate::types::FloatingPointRange::new(min, max, step),
1585 );
1586 }
1587 self
1588 }
1589
1590 #[must_use]
1595 pub fn declare(self) -> bool {
1596 self.server.declare_with_descriptor(
1597 self.node,
1598 self.name.as_str(),
1599 self.value,
1600 self.descriptor,
1601 )
1602 }
1603}
1604
1605#[cfg(test)]
1606mod tests {
1607 use super::*;
1608
1609 const NODE: NodeKey = NodeKey::PRIMARY;
1611 use crate::types::{ParameterDescriptor, ParameterType};
1612
1613 #[test]
1621 fn each_truncated_description_is_handed_over_once() {
1622 let long = "y".repeat(crate::MAX_PARAM_DESCRIPTION_LEN + 1);
1623 let other = NodeKey::new(1);
1624 let mut storage = ParameterStorage::<8>::new();
1625 let mut server = ParameterServer::new_in(storage.as_table());
1626 assert_eq!(server.take_truncated_descriptions(|_, _| ()), 0);
1627
1628 let d = ParameterDescriptor::new("plain", ParameterType::Integer)
1629 .unwrap()
1630 .with_description(&long);
1631 assert!(server.declare_with_descriptor(NODE, "plain", ParameterValue::Integer(1), Some(d)));
1632 let fits = ParameterDescriptor::new("short", ParameterType::Integer)
1633 .unwrap()
1634 .with_description("");
1635 assert!(server.declare_with_descriptor(
1636 NODE,
1637 "short",
1638 ParameterValue::Integer(1),
1639 Some(fits)
1640 ));
1641 crate::ParameterBuilder::<i64>::new(&mut server, other, "typed")
1642 .default(3)
1643 .description(&long)
1644 .optional()
1645 .expect("a long description no longer refuses the declaration");
1646 assert!(
1647 LegacyParameterBuilder::new(&mut server, NODE, "legacy", ParameterValue::Bool(true))
1648 .unwrap()
1649 .description(&long)
1650 .declare()
1651 );
1652
1653 let mut seen: heapless::Vec<(usize, heapless::String<16>), 8> = heapless::Vec::new();
1654 let n = server.take_truncated_descriptions(|node, name| {
1655 let mut s = heapless::String::new();
1656 s.push_str(name).unwrap();
1657 seen.push((node.index(), s)).unwrap();
1658 });
1659 assert_eq!(n, 3, "{seen:?}");
1660 for (node, name) in [(0, "plain"), (1, "typed"), (0, "legacy")] {
1661 assert!(
1662 seen.iter().any(|(k, s)| *k == node && s.as_str() == name),
1663 "{name} on node {node} not reported: {seen:?}"
1664 );
1665 }
1666 assert!(!seen.iter().any(|(_, s)| s.as_str() == "short"));
1667 assert_eq!(
1668 server.take_truncated_descriptions(|_, _| ()),
1669 0,
1670 "once per parameter"
1671 );
1672 let kept = server.get_descriptor(other, "typed").unwrap();
1673 assert_eq!(kept.description.len(), crate::MAX_PARAM_DESCRIPTION_LEN);
1674 }
1675
1676 #[test]
1683 fn two_nodes_hold_independent_values_for_one_name() {
1684 let talker = NodeKey::new(0);
1685 let listener = NodeKey::new(1);
1686 let mut storage: ParameterStorage = ParameterStorage::new();
1687 let mut server = ParameterServer::new_in(storage.as_table());
1688
1689 assert!(server.declare(talker, "rate", ParameterValue::Integer(10)));
1690 assert!(
1692 server.declare(listener, "rate", ParameterValue::Integer(20)),
1693 "a sibling node's identical name is a DIFFERENT parameter"
1694 );
1695
1696 assert_eq!(server.get_integer(talker, "rate"), Some(10));
1697 assert_eq!(server.get_integer(listener, "rate"), Some(20));
1698
1699 assert_eq!(
1701 server.set(talker, "rate", ParameterValue::Integer(11)),
1702 SetParameterResult::Success
1703 );
1704 assert_eq!(server.get_integer(talker, "rate"), Some(11));
1705 assert_eq!(server.get_integer(listener, "rate"), Some(20));
1706
1707 assert_eq!(server.len(talker), 1);
1709 assert_eq!(server.len(listener), 1);
1710 assert_eq!(server.total_len(), 2, "one shared arena, two entries");
1711 }
1712
1713 #[test]
1715 fn a_sibling_cannot_read_or_write_a_name_it_never_declared() {
1716 let a = NodeKey::new(0);
1717 let b = NodeKey::new(3);
1718 let mut storage: ParameterStorage = ParameterStorage::new();
1719 let mut server = ParameterServer::new_in(storage.as_table());
1720 assert!(server.declare(a, "gain", ParameterValue::Double(1.0)));
1721
1722 assert!(!server.has(b, "gain"));
1723 assert!(server.get(b, "gain").is_none());
1724 assert_eq!(
1725 server.set(b, "gain", ParameterValue::Double(2.0)),
1726 SetParameterResult::NotFound
1727 );
1728 assert_eq!(
1729 server.check_apply(b, "gain", &ParameterValue::Double(2.0)),
1730 SetParameterResult::Undeclared
1731 );
1732 assert_eq!(server.get_double(a, "gain"), Some(1.0));
1734 }
1735
1736 #[test]
1740 fn allow_undeclared_does_not_leak_to_a_sibling_node() {
1741 let permissive = NodeKey::new(1);
1742 let strict = NodeKey::new(2);
1743 let mut storage: ParameterStorage = ParameterStorage::new();
1744 let mut server = ParameterServer::new_in(storage.as_table());
1745 server.set_allow_undeclared(permissive, true);
1746
1747 assert!(server.allows_undeclared(permissive));
1748 assert!(!server.allows_undeclared(strict));
1749
1750 assert_eq!(
1751 server.apply(permissive, "fresh", ParameterValue::Integer(7)),
1752 SetParameterResult::Success
1753 );
1754 assert_eq!(
1755 server.apply(strict, "fresh", ParameterValue::Integer(7)),
1756 SetParameterResult::Undeclared,
1757 "the sibling still refuses a name IT never declared"
1758 );
1759 assert_eq!(server.get_integer(permissive, "fresh"), Some(7));
1760 assert!(!server.has(strict, "fresh"));
1761 }
1762
1763 #[test]
1767 fn the_allow_undeclared_flag_covers_the_whole_key_space() {
1768 let mut storage: ParameterStorage = ParameterStorage::new();
1769 let mut server = ParameterServer::new_in(storage.as_table());
1770 for raw in [0u8, 63, 64, 127, 128, 191, 192, 255] {
1771 let node = NodeKey::new(raw);
1772 assert!(!server.allows_undeclared(node), "raw {raw}");
1773 server.set_allow_undeclared(node, true);
1774 assert!(server.allows_undeclared(node), "raw {raw}");
1775 server.set_allow_undeclared(node, false);
1776 assert!(!server.allows_undeclared(node), "raw {raw}");
1777 }
1778 }
1779
1780 #[test]
1784 fn the_slot_arena_is_shared_across_nodes() {
1785 let a = NodeKey::new(0);
1786 let b = NodeKey::new(1);
1787 let mut storage = ParameterStorage::<2>::new();
1788 let mut server = ParameterServer::new_in(storage.as_table());
1789 assert!(server.declare(a, "one", ParameterValue::Integer(1)));
1790 assert!(server.declare(b, "one", ParameterValue::Integer(2)));
1791 assert!(server.is_full());
1792 assert!(!server.declare(a, "two", ParameterValue::Integer(3)));
1793 assert_eq!(server.total_len(), 2);
1794 assert_eq!(server.len(a), 1);
1795 assert_eq!(server.len(b), 1);
1796 }
1797
1798 #[test]
1800 fn remove_is_keyed_by_node() {
1801 let a = NodeKey::new(0);
1802 let b = NodeKey::new(1);
1803 let mut storage: ParameterStorage = ParameterStorage::new();
1804 let mut server = ParameterServer::new_in(storage.as_table());
1805 assert!(server.declare(a, "x", ParameterValue::Integer(1)));
1806 assert!(server.declare(b, "x", ParameterValue::Integer(2)));
1807
1808 assert!(server.remove(a, "x"));
1809 assert!(!server.has(a, "x"));
1810 assert_eq!(server.get_integer(b, "x"), Some(2));
1811 assert_eq!(server.total_len(), 1);
1812 }
1813
1814 #[test]
1817 fn set_rejects_a_value_off_the_integer_step() {
1818 let mut storage: ParameterStorage = ParameterStorage::new();
1819 let mut server = ParameterServer::new_in(storage.as_table());
1820 let desc = ParameterDescriptor::new("rate", ParameterType::Integer)
1821 .unwrap()
1822 .with_integer_range(0, 100, 10);
1823 assert!(server.declare_with_descriptor(
1824 NODE,
1825 "rate",
1826 ParameterValue::Integer(20),
1827 Some(desc)
1828 ));
1829
1830 assert_eq!(
1831 server.set(NODE, "rate", ParameterValue::Integer(30)),
1832 SetParameterResult::Success
1833 );
1834 assert_eq!(
1836 server.set(NODE, "rate", ParameterValue::Integer(35)),
1837 SetParameterResult::OutOfRange
1838 );
1839 assert_eq!(server.get_integer(NODE, "rate"), Some(30));
1840 }
1841
1842 #[test]
1843 fn set_rejects_a_value_off_the_float_step() {
1844 let mut storage: ParameterStorage = ParameterStorage::new();
1845 let mut server = ParameterServer::new_in(storage.as_table());
1846 let desc = ParameterDescriptor::new("gain", ParameterType::Double)
1847 .unwrap()
1848 .with_float_range(0.0, 1.0, 0.25);
1849 assert!(server.declare_with_descriptor(
1850 NODE,
1851 "gain",
1852 ParameterValue::Double(0.5),
1853 Some(desc)
1854 ));
1855
1856 assert_eq!(
1857 server.set_double(NODE, "gain", 0.75),
1858 SetParameterResult::Success
1859 );
1860 assert_eq!(
1861 server.set_double(NODE, "gain", 0.6),
1862 SetParameterResult::OutOfRange
1863 );
1864 assert_eq!(server.get_double(NODE, "gain"), Some(0.75));
1865 }
1866
1867 #[test]
1868 fn declare_refuses_an_ill_formed_range() {
1869 let mut storage: ParameterStorage = ParameterStorage::new();
1870 let mut server = ParameterServer::new_in(storage.as_table());
1871 let desc = ParameterDescriptor::new("bad", ParameterType::Integer)
1872 .unwrap()
1873 .with_integer_range(0, 100, -1);
1874 assert!(!server.declare_with_descriptor(
1875 NODE,
1876 "bad",
1877 ParameterValue::Integer(0),
1878 Some(desc.clone())
1879 ));
1880 assert!(!server.has(NODE, "bad"));
1881 assert_eq!(
1882 server.declare_parameter(NODE, desc, Some(&ParameterValue::Integer(0))),
1883 Err(SetParameterResult::InvalidRange)
1884 );
1885 assert!(!server.has(NODE, "bad"));
1886 }
1887
1888 #[test]
1889 fn declare_refuses_a_default_its_own_range_rejects() {
1890 let mut storage: ParameterStorage = ParameterStorage::new();
1891 let mut server = ParameterServer::new_in(storage.as_table());
1892 let desc = ParameterDescriptor::new("odd", ParameterType::Integer)
1893 .unwrap()
1894 .with_integer_range(0, 100, 2);
1895 assert!(!server.declare_with_descriptor(
1896 NODE,
1897 "odd",
1898 ParameterValue::Integer(3),
1899 Some(desc.clone())
1900 ));
1901 assert!(server.declare_with_descriptor(
1902 NODE,
1903 "odd",
1904 ParameterValue::Integer(4),
1905 Some(desc)
1906 ));
1907 }
1908
1909 #[test]
1912 fn apply_rejects_an_undeclared_name_by_default() {
1913 let mut storage: ParameterStorage = ParameterStorage::new();
1914 let mut server = ParameterServer::new_in(storage.as_table());
1915 assert!(server.declare(NODE, "max_speed", ParameterValue::Double(1.0)));
1916 assert!(!server.allows_undeclared(NODE));
1917
1918 assert_eq!(
1921 server.check_apply(NODE, "max_speeed", &ParameterValue::Double(5.0)),
1922 SetParameterResult::Undeclared
1923 );
1924 assert_eq!(
1925 server.apply(NODE, "max_speeed", ParameterValue::Double(5.0)),
1926 SetParameterResult::Undeclared
1927 );
1928 assert!(!server.has(NODE, "max_speeed"));
1929 assert_eq!(server.len(NODE), 1);
1930 assert_eq!(server.get_double(NODE, "max_speed"), Some(1.0));
1931
1932 assert_eq!(
1934 server.apply(NODE, "max_speed", ParameterValue::Double(5.0)),
1935 SetParameterResult::Success
1936 );
1937 assert_eq!(server.get_double(NODE, "max_speed"), Some(5.0));
1938 }
1939
1940 #[test]
1941 fn apply_declares_an_unknown_name_under_allow_undeclared() {
1942 let mut storage: ParameterStorage = ParameterStorage::new();
1943 let mut server = ParameterServer::new_in(storage.as_table());
1944 server.set_allow_undeclared(NODE, true);
1945 assert!(server.allows_undeclared(NODE));
1946
1947 assert_eq!(
1948 server.check_apply(NODE, "fresh", &ParameterValue::Integer(7)),
1949 SetParameterResult::Success
1950 );
1951 assert_eq!(
1952 server.apply(NODE, "fresh", ParameterValue::Integer(7)),
1953 SetParameterResult::Success
1954 );
1955 assert_eq!(server.get_integer(NODE, "fresh"), Some(7));
1956 }
1957
1958 #[test]
1959 fn apply_under_allow_undeclared_still_reports_a_full_store() {
1960 let mut storage: ParameterStorage<1> = ParameterStorage::new();
1961 let mut server = ParameterServer::new_in(storage.as_table());
1962 server.set_allow_undeclared(NODE, true);
1963 assert!(server.declare(NODE, "only", ParameterValue::Bool(true)));
1964 assert_eq!(
1965 server.check_apply(NODE, "second", &ParameterValue::Bool(true)),
1966 SetParameterResult::StorageFull
1967 );
1968 assert_eq!(
1969 server.apply(NODE, "second", ParameterValue::Bool(true)),
1970 SetParameterResult::StorageFull
1971 );
1972 }
1973
1974 #[test]
1975 fn apply_enforces_the_existing_entry_rules_through_the_same_predicate() {
1976 let mut storage: ParameterStorage = ParameterStorage::new();
1977 let mut server = ParameterServer::new_in(storage.as_table());
1978 let desc = ParameterDescriptor::new("rate", ParameterType::Integer)
1979 .unwrap()
1980 .with_integer_range(0, 100, 10);
1981 assert!(server.declare_with_descriptor(
1982 NODE,
1983 "rate",
1984 ParameterValue::Integer(20),
1985 Some(desc)
1986 ));
1987 assert_eq!(
1988 server.check_apply(NODE, "rate", &ParameterValue::Integer(35)),
1989 SetParameterResult::OutOfRange
1990 );
1991 assert_eq!(
1992 server.apply(NODE, "rate", ParameterValue::Integer(35)),
1993 SetParameterResult::OutOfRange
1994 );
1995 assert_eq!(
1996 server.apply(NODE, "rate", ParameterValue::Double(30.0)),
1997 SetParameterResult::TypeMismatch
1998 );
1999 assert_eq!(server.get_integer(NODE, "rate"), Some(20));
2000 }
2001
2002 #[test]
2003 fn test_new_server() {
2004 let mut storage: ParameterStorage = ParameterStorage::new();
2005 let server = ParameterServer::new_in(storage.as_table());
2006 assert_eq!(server.len(NODE), 0);
2007 assert!(server.is_empty(NODE));
2008 }
2009
2010 #[test]
2011 #[allow(clippy::approx_constant)]
2012 fn test_declare_and_get() {
2013 let mut storage: ParameterStorage = ParameterStorage::new();
2014 let mut server = ParameterServer::new_in(storage.as_table());
2015
2016 assert!(server.declare(NODE, "my_bool", ParameterValue::Bool(true)));
2017 assert!(server.declare(NODE, "my_int", ParameterValue::Integer(42)));
2018 assert!(server.declare(NODE, "my_double", ParameterValue::Double(3.14)));
2019
2020 assert_eq!(server.len(NODE), 3);
2021 assert!(!server.is_empty(NODE));
2022
2023 assert_eq!(server.get_bool(NODE, "my_bool"), Some(true));
2024 assert_eq!(server.get_integer(NODE, "my_int"), Some(42));
2025 assert_eq!(server.get_double(NODE, "my_double"), Some(3.14));
2026 }
2027
2028 #[test]
2029 fn test_unset_clears_the_value_and_reports_success() {
2030 let mut storage: ParameterStorage = ParameterStorage::new();
2036 let mut server = ParameterServer::new_in(storage.as_table());
2037 assert!(server.declare(NODE, "count", ParameterValue::Integer(0)));
2038 assert_eq!(
2039 server.set_integer(NODE, "count", 10),
2040 SetParameterResult::Success
2041 );
2042 assert_eq!(server.get_integer(NODE, "count"), Some(10));
2043
2044 assert_eq!(server.unset(NODE, "count"), SetParameterResult::Success);
2045 assert_eq!(server.get_integer(NODE, "count"), None);
2046 }
2047
2048 #[test]
2049 fn test_set_parameter() {
2050 let mut storage: ParameterStorage = ParameterStorage::new();
2051 let mut server = ParameterServer::new_in(storage.as_table());
2052 assert!(server.declare(NODE, "count", ParameterValue::Integer(0)));
2053
2054 assert_eq!(
2055 server.set_integer(NODE, "count", 10),
2056 SetParameterResult::Success
2057 );
2058 assert_eq!(server.get_integer(NODE, "count"), Some(10));
2059 }
2060
2061 #[test]
2062 fn test_set_nonexistent() {
2063 let mut storage: ParameterStorage = ParameterStorage::new();
2064 let mut server = ParameterServer::new_in(storage.as_table());
2065 assert_eq!(
2066 server.set(NODE, "nonexistent", ParameterValue::Integer(1)),
2067 SetParameterResult::NotFound
2068 );
2069 }
2070
2071 #[test]
2072 fn test_read_only_parameter() {
2073 let mut storage: ParameterStorage = ParameterStorage::new();
2074 let mut server = ParameterServer::new_in(storage.as_table());
2075
2076 let desc = ParameterDescriptor::new("version", ParameterType::String)
2077 .unwrap()
2078 .with_read_only(true);
2079
2080 assert!(server.declare_with_descriptor(
2081 NODE,
2082 "version",
2083 ParameterValue::from_string("1.0.0").unwrap(),
2084 Some(desc),
2085 ));
2086
2087 assert_eq!(
2088 server.set_string(NODE, "version", "2.0.0"),
2089 SetParameterResult::ReadOnly
2090 );
2091 assert_eq!(server.get_string(NODE, "version"), Some("1.0.0"));
2092 }
2093
2094 #[test]
2095 fn test_range_constraints() {
2096 let mut storage: ParameterStorage = ParameterStorage::new();
2097 let mut server = ParameterServer::new_in(storage.as_table());
2098
2099 let desc = ParameterDescriptor::new("speed", ParameterType::Double)
2100 .unwrap()
2101 .with_float_range(0.0, 10.0, 0.0);
2102
2103 assert!(server.declare_with_descriptor(
2104 NODE,
2105 "speed",
2106 ParameterValue::Double(5.0),
2107 Some(desc)
2108 ));
2109
2110 assert_eq!(
2112 server.set_double(NODE, "speed", 8.0),
2113 SetParameterResult::Success
2114 );
2115
2116 assert_eq!(
2118 server.set_double(NODE, "speed", 15.0),
2119 SetParameterResult::OutOfRange
2120 );
2121 assert_eq!(server.get_double(NODE, "speed"), Some(8.0)); }
2123
2124 #[test]
2125 fn test_remove_parameter() {
2126 let mut storage: ParameterStorage = ParameterStorage::new();
2127 let mut server = ParameterServer::new_in(storage.as_table());
2128 assert!(server.declare(NODE, "temp", ParameterValue::Double(25.0)));
2129
2130 assert!(server.has(NODE, "temp"));
2131 assert!(server.remove(NODE, "temp"));
2132 assert!(!server.has(NODE, "temp"));
2133 assert!(!server.remove(NODE, "temp")); }
2135
2136 #[test]
2137 fn test_list_parameters() {
2138 let mut storage: ParameterStorage = ParameterStorage::new();
2139 let mut server = ParameterServer::new_in(storage.as_table());
2140 assert!(server.declare(NODE, "robot.speed", ParameterValue::Double(1.0)));
2141 assert!(server.declare(
2142 NODE,
2143 "robot.name",
2144 ParameterValue::from_string("bot1").unwrap(),
2145 ));
2146 assert!(server.declare(NODE, "sensor.range", ParameterValue::Double(10.0)));
2147
2148 let robot_params: heapless::Vec<&str, 8> =
2149 server.list_with_prefix(NODE, "robot.").collect();
2150 assert_eq!(robot_params.len(), 2);
2151 }
2152
2153 #[test]
2154 fn test_set_or_declare() {
2155 let mut storage: ParameterStorage = ParameterStorage::new();
2156 let mut server = ParameterServer::new_in(storage.as_table());
2157
2158 assert_eq!(
2160 server.set_or_declare(NODE, "new_param", ParameterValue::Integer(1)),
2161 SetParameterResult::Success
2162 );
2163 assert_eq!(server.get_integer(NODE, "new_param"), Some(1));
2164
2165 assert_eq!(
2167 server.set_or_declare(NODE, "new_param", ParameterValue::Integer(2)),
2168 SetParameterResult::Success
2169 );
2170 assert_eq!(server.get_integer(NODE, "new_param"), Some(2));
2171 }
2172
2173 #[test]
2174 fn test_duplicate_declare() {
2175 let mut storage: ParameterStorage = ParameterStorage::new();
2176 let mut server = ParameterServer::new_in(storage.as_table());
2177 assert!(server.declare(NODE, "param", ParameterValue::Integer(1)));
2178 assert!(!server.declare(NODE, "param", ParameterValue::Integer(2))); assert_eq!(server.get_integer(NODE, "param"), Some(1)); }
2181
2182 #[test]
2188 #[allow(clippy::absurd_extreme_comparisons, clippy::unnecessary_min_or_max)]
2195 fn descriptor_verbs_attach_to_a_declared_parameter() {
2196 let mut storage: ParameterStorage = ParameterStorage::new();
2197 let mut server = ParameterServer::new_in(storage.as_table());
2198 assert!(server.declare(NODE, "rate", ParameterValue::Double(2.0)));
2199
2200 assert!(server.get_descriptor(NODE, "rate").is_none());
2203 assert!(server.set_parameter_description(NODE, "rate", "publish rate", "hz, positive"));
2204 assert!(server.set_parameter_float_range(NODE, "rate", 0.0, 10.0, 0.0));
2205
2206 let desc = server.get_descriptor(NODE, "rate").expect("descriptor");
2207 assert_eq!(desc.description.as_str(), "publish rate");
2208 let constraints_fit = "hz, positive".len() <= crate::MAX_PARAM_CONSTRAINTS_LEN;
2214 assert_eq!(
2215 desc.additional_constraints.as_str(),
2216 &"hz, positive"[..crate::MAX_PARAM_CONSTRAINTS_LEN.min("hz, positive".len())]
2217 );
2218 assert_eq!(desc.param_type, ParameterType::Double);
2219
2220 assert_eq!(
2222 server.apply(NODE, "rate", ParameterValue::Double(50.0)),
2223 SetParameterResult::OutOfRange
2224 );
2225 assert_eq!(
2226 server.apply(NODE, "rate", ParameterValue::Double(5.0)),
2227 SetParameterResult::Success
2228 );
2229
2230 assert!(server.set_parameter_read_only(NODE, "rate", true));
2232 assert_eq!(
2233 server.apply(NODE, "rate", ParameterValue::Double(6.0)),
2234 SetParameterResult::ReadOnly
2235 );
2236
2237 let mut reported = 0usize;
2241 server.take_truncated_descriptions(|_, _| reported += 1);
2242 assert_eq!(
2243 reported,
2244 usize::from(!constraints_fit),
2245 "a constraints string that did not fit the capacity went unreported"
2246 );
2247
2248 assert!(!server.set_parameter_description(NODE, "absent", "d", "c"));
2251 assert!(!server.set_parameter_read_only(NODE, "absent", true));
2252 assert!(!server.set_parameter_integer_range(NODE, "absent", 0, 1, 1));
2253 }
2254
2255 #[test]
2259 fn a_range_must_fit_the_value_it_is_attached_to() {
2260 let mut storage: ParameterStorage = ParameterStorage::new();
2261 let mut server = ParameterServer::new_in(storage.as_table());
2262 assert!(server.declare(NODE, "depth", ParameterValue::Integer(100)));
2263
2264 assert!(!server.set_parameter_integer_range(NODE, "depth", 0, 10, 1));
2265 assert!(server.get_descriptor(NODE, "depth").is_none());
2266
2267 assert!(!server.set_parameter_integer_range(NODE, "depth", 10, 0, 1));
2269 assert!(!server.set_parameter_integer_range(NODE, "depth", 0, 1000, -1));
2271
2272 assert!(server.set_parameter_integer_range(NODE, "depth", 0, 1000, 1));
2273 }
2274
2275 #[test]
2278 fn undeclare_frees_the_slot_for_that_node_only() {
2279 let mut storage: ParameterStorage = ParameterStorage::new();
2280 let mut server = ParameterServer::new_in(storage.as_table());
2281 let other = NodeKey::new(1);
2282 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2283 assert!(server.declare(other, "rate", ParameterValue::Integer(2)));
2284 assert_eq!(server.total_len(), 2);
2285
2286 assert!(server.remove(NODE, "rate"));
2287 assert!(!server.has(NODE, "rate"));
2288 assert!(server.has(other, "rate"));
2289 assert_eq!(server.total_len(), 1);
2290 assert!(!server.remove(NODE, "rate"));
2291
2292 assert!(server.declare(NODE, "rate", ParameterValue::Integer(3)));
2293 assert_eq!(server.get_integer(NODE, "rate"), Some(3));
2294 }
2295
2296 fn refuse_everything(
2297 _n: NodeKey,
2298 _name: &str,
2299 _old: Option<&ParameterValue>,
2300 _new: Option<&ParameterValue>,
2301 _ctx: OnSetContext,
2302 ) -> bool {
2303 false
2304 }
2305
2306 fn refuse_if_ctx_matches(
2307 _n: NodeKey,
2308 _name: &str,
2309 _old: Option<&ParameterValue>,
2310 new: Option<&ParameterValue>,
2311 ctx: OnSetContext,
2312 ) -> bool {
2313 new.and_then(ParameterValue::as_integer) != Some(ctx.a as i64)
2314 }
2315
2316 #[test]
2320 fn an_on_set_callback_vetoes_the_write_it_refuses() {
2321 let mut storage: ParameterStorage = ParameterStorage::new();
2322 let mut server = ParameterServer::new_in(storage.as_table());
2323 let other = NodeKey::new(1);
2324 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2325 assert!(server.declare(other, "rate", ParameterValue::Integer(1)));
2326
2327 let h = server
2328 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
2329 .expect("a free slot");
2330
2331 assert_eq!(
2332 server.apply(NODE, "rate", ParameterValue::Integer(9)),
2333 SetParameterResult::Rejected
2334 );
2335 assert_eq!(server.get_integer(NODE, "rate"), Some(1));
2336 assert_eq!(
2338 server.apply(other, "rate", ParameterValue::Integer(9)),
2339 SetParameterResult::Success
2340 );
2341
2342 assert!(server.remove_on_set_parameters_callback(h));
2343 assert!(!server.remove_on_set_parameters_callback(h));
2344 assert_eq!(
2345 server.apply(NODE, "rate", ParameterValue::Integer(9)),
2346 SetParameterResult::Success
2347 );
2348 }
2349
2350 #[derive(Default)]
2352 struct Seen {
2353 calls: u32,
2354 old: Option<i64>,
2355 new: Option<i64>,
2356 old_present: bool,
2357 new_present: bool,
2358 }
2359
2360 fn record(
2361 _n: NodeKey,
2362 _name: &str,
2363 old: Option<&ParameterValue>,
2364 new: Option<&ParameterValue>,
2365 ctx: OnSetContext,
2366 ) -> bool {
2367 let seen = unsafe { &mut *(ctx.a as *mut Seen) };
2369 seen.calls += 1;
2370 seen.old_present = old.is_some();
2371 seen.new_present = new.is_some();
2372 seen.old = old.and_then(ParameterValue::as_integer);
2373 seen.new = new.and_then(ParameterValue::as_integer);
2374 true
2375 }
2376
2377 #[test]
2380 fn the_event_log_merges_by_name_and_counts_overflow() {
2381 let mut storage: ParameterStorage = ParameterStorage::new();
2382 let mut server = ParameterServer::new_in(storage.as_table());
2383 assert!(server.declare(NODE, "quiet", ParameterValue::Integer(0)));
2384 assert!(server.pop_parameter_event().is_none(), "off by default");
2385
2386 server.set_parameter_events(NODE, true);
2387 assert!(server.declare(NODE, "a", ParameterValue::Integer(1)));
2388 assert_eq!(
2389 server.set(NODE, "a", ParameterValue::Integer(2)),
2390 SetParameterResult::Success
2391 );
2392 assert!(server.remove(NODE, "a"));
2393 let e = server.pop_parameter_event().expect("one merged entry");
2394 assert_eq!(
2395 (e.name.as_str(), e.kind),
2396 ("a", ParameterEventKind::Deleted)
2397 );
2398 assert!(server.pop_parameter_event().is_none());
2399
2400 for i in 0..PARAMETER_EVENT_LOG_LEN + 2 {
2401 let mut name = heapless::String::<8>::new();
2402 core::fmt::Write::write_fmt(&mut name, format_args!("p{i}")).unwrap();
2403 assert!(server.declare(NODE, &name, ParameterValue::Integer(0)));
2404 }
2405 assert_eq!(server.take_parameter_events_dropped(), 2);
2406 assert_eq!(
2407 server.take_parameter_events_dropped(),
2408 0,
2409 "the count resets"
2410 );
2411 }
2412
2413 #[test]
2416 fn the_on_set_hook_sees_old_and_new() {
2417 let mut storage: ParameterStorage = ParameterStorage::new();
2418 let mut server = ParameterServer::new_in(storage.as_table());
2419 let mut seen = Seen::default();
2420 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2421 server
2422 .add_on_set_parameters_callback(NODE, record, OnSetContext::new(&raw mut seen as usize))
2423 .expect("a free slot");
2424
2425 assert_eq!(
2426 server.apply(NODE, "rate", ParameterValue::Integer(2)),
2427 SetParameterResult::Success
2428 );
2429 assert_eq!((seen.old, seen.new), (Some(1), Some(2)));
2430
2431 server.set_allow_undeclared(NODE, true);
2432 assert_eq!(
2433 server.apply(NODE, "fresh", ParameterValue::Integer(7)),
2434 SetParameterResult::Success
2435 );
2436 assert!(!seen.old_present, "a create has no old value");
2437 assert_eq!(seen.new, Some(7));
2438 }
2439
2440 #[test]
2443 fn a_local_set_skips_read_only_and_range_but_not_type() {
2444 let mut storage: ParameterStorage = ParameterStorage::new();
2445 let mut server = ParameterServer::new_in(storage.as_table());
2446 let mut seen = Seen::default();
2447 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2448 assert!(server.set_parameter_integer_range(NODE, "rate", 0, 10, 1));
2449 assert!(server.set_parameter_read_only(NODE, "rate", true));
2450 server
2451 .add_on_set_parameters_callback(NODE, record, OnSetContext::new(&raw mut seen as usize))
2452 .expect("a free slot");
2453
2454 assert_eq!(
2456 server.apply(NODE, "rate", ParameterValue::Integer(50)),
2457 SetParameterResult::ReadOnly
2458 );
2459 assert_eq!(seen.calls, 0, "a refused remote write reached the hook");
2460
2461 assert_eq!(
2463 server.set_local(NODE, "rate", ParameterValue::Integer(50)),
2464 SetParameterResult::Success
2465 );
2466 assert_eq!((seen.old, seen.new), (Some(1), Some(50)));
2467 assert_eq!(server.get_integer(NODE, "rate"), Some(50));
2468
2469 assert_eq!(
2470 server.set_local(NODE, "rate", ParameterValue::Double(1.0)),
2471 SetParameterResult::TypeMismatch
2472 );
2473 assert_eq!(
2474 server.set_local(NODE, "nope", ParameterValue::Integer(1)),
2475 SetParameterResult::NotFound
2476 );
2477 assert!(
2478 server
2479 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
2480 .is_some()
2481 );
2482 assert_eq!(
2483 server.set_local(NODE, "rate", ParameterValue::Integer(3)),
2484 SetParameterResult::Rejected
2485 );
2486 assert_eq!(server.get_integer(NODE, "rate"), Some(50));
2487 }
2488
2489 #[test]
2492 fn not_set_deletes_only_on_a_node_that_asked() {
2493 let mut storage: ParameterStorage = ParameterStorage::new();
2494 let mut server = ParameterServer::new_in(storage.as_table());
2495 let other = NodeKey::new(1);
2496 let mut seen = Seen::default();
2497 assert!(server.declare(NODE, "rate", ParameterValue::Integer(4)));
2498 assert!(server.declare(other, "rate", ParameterValue::Integer(4)));
2499 server.set_delete_on_not_set(NODE, true);
2500 server
2501 .add_on_set_parameters_callback(NODE, record, OnSetContext::new(&raw mut seen as usize))
2502 .expect("a free slot");
2503
2504 assert_eq!(
2505 server.apply(NODE, "rate", ParameterValue::NotSet),
2506 SetParameterResult::Success
2507 );
2508 assert!(!server.has(NODE, "rate"));
2509 assert_eq!((seen.old, seen.new_present), (Some(4), false));
2510
2511 let _ = server.apply(other, "rate", ParameterValue::NotSet);
2514 assert!(
2515 server.has(other, "rate"),
2516 "the rclcpp node's parameter was deleted"
2517 );
2518
2519 assert!(server.declare(NODE, "ro", ParameterValue::Integer(1)));
2520 assert!(server.set_parameter_read_only(NODE, "ro", true));
2521 assert_eq!(
2522 server.apply(NODE, "ro", ParameterValue::NotSet),
2523 SetParameterResult::ReadOnly
2524 );
2525 assert!(server.has(NODE, "ro"));
2526 }
2527
2528 #[test]
2533 fn apply_staged_is_apply_from_a_slot() {
2534 fn reject(
2535 _: NodeKey,
2536 name: &str,
2537 _: Option<&ParameterValue>,
2538 _: Option<&ParameterValue>,
2539 _: OnSetContext,
2540 ) -> bool {
2541 name != "vetoed"
2542 }
2543 fn prepared(storage: &mut ParameterStorage<4>) -> ParameterServer<'_> {
2544 let mut server = ParameterServer::new_in(storage.as_table());
2545 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2546 assert!(
2547 server.declare_with_descriptor(
2548 NODE,
2549 "ro",
2550 ParameterValue::Integer(1),
2551 Some(
2552 ParameterDescriptor::new("ro", ParameterType::Integer)
2553 .unwrap()
2554 .with_read_only(true)
2555 ),
2556 )
2557 );
2558 assert!(server.declare_with_descriptor(
2559 NODE,
2560 "typed",
2561 ParameterValue::Integer(1),
2562 Some(ParameterDescriptor::new("typed", ParameterType::Integer).unwrap()),
2563 ));
2564 assert!(server.declare(NODE, "vetoed", ParameterValue::Integer(1)));
2565 server
2566 .add_on_set_parameters_callback(NODE, reject, OnSetContext::new(0))
2567 .expect("a free slot");
2568 server
2569 }
2570 type Row = (&'static str, fn() -> ParameterValue, bool, bool);
2573 let rows: [Row; 7] = [
2574 ("rate", || ParameterValue::Integer(7), false, false),
2575 ("ro", || ParameterValue::Integer(7), false, false),
2576 ("typed", || ParameterValue::Bool(true), false, false),
2577 ("vetoed", || ParameterValue::Integer(7), false, false),
2578 ("fresh", || ParameterValue::Integer(7), false, false),
2579 ("fresh", || ParameterValue::Integer(7), true, false),
2581 ("rate", || ParameterValue::NotSet, false, true),
2583 ];
2584 for (name, value, allow, delete) in rows {
2585 let mut a_storage: ParameterStorage<4> = ParameterStorage::new();
2586 let mut b_storage: ParameterStorage<4> = ParameterStorage::new();
2587 let mut by_value = prepared(&mut a_storage);
2588 let mut by_slot = prepared(&mut b_storage);
2589 for s in [&mut by_value, &mut by_slot] {
2590 s.set_allow_undeclared(NODE, allow);
2591 s.set_delete_on_not_set(NODE, delete);
2592 }
2593 let want = by_value.apply(NODE, name, value());
2594 let mut slot = value();
2595 let got = by_slot.apply_staged(NODE, name, &mut slot);
2596 assert_eq!(got, want, "verdict for `{name}`");
2597 for probe in ["rate", "ro", "typed", "vetoed", "fresh"] {
2598 assert_eq!(
2599 by_slot.get(NODE, probe).map(ParameterValue::param_type),
2600 by_value.get(NODE, probe).map(ParameterValue::param_type),
2601 "`{probe}` after setting `{name}`"
2602 );
2603 assert_eq!(
2604 by_slot.get_integer(NODE, probe),
2605 by_value.get_integer(NODE, probe),
2606 "`{probe}` after setting `{name}`"
2607 );
2608 }
2609 }
2610
2611 let mut storage: ParameterStorage<4> = ParameterStorage::new();
2613 let mut server = ParameterServer::new_in(storage.as_table());
2614 server.set_allow_undeclared(NODE, true);
2615 let mut slot = ParameterValue::Double(2.5);
2616 assert_eq!(
2617 server.apply_staged(NODE, "fresh", &mut slot),
2618 SetParameterResult::Success
2619 );
2620 assert_eq!(server.get_double(NODE, "fresh"), Some(2.5));
2621 assert_eq!(server.len(NODE), 1);
2622
2623 let mut slot = ParameterValue::Double(4.0);
2625 assert!(server.apply_staged(NODE, "fresh", &mut slot).is_success());
2626 assert_eq!(slot.as_double(), Some(2.5));
2627 assert_eq!(server.get_double(NODE, "fresh"), Some(4.0));
2628 }
2629
2630 #[test]
2634 fn the_on_set_registry_refuses_past_its_stated_capacity() {
2635 let mut storage: ParameterStorage = ParameterStorage::new();
2636 let mut server = ParameterServer::new_in(storage.as_table());
2637 assert!(server.declare(NODE, "rate", ParameterValue::Integer(1)));
2638
2639 assert_eq!(server.on_set_callbacks_free(), MAX_ON_SET_CALLBACKS);
2640 let mut handles = heapless::Vec::<_, MAX_ON_SET_CALLBACKS>::new();
2641 for i in 0..MAX_ON_SET_CALLBACKS {
2642 let h = server
2643 .add_on_set_parameters_callback(
2644 NODE,
2645 refuse_if_ctx_matches,
2646 OnSetContext::new(40 + i),
2647 )
2648 .expect("within capacity");
2649 handles.push(h).expect("bounded by the same constant");
2650 }
2651 assert_eq!(server.on_set_callbacks_free(), 0);
2652 assert!(
2653 server
2654 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
2655 .is_none()
2656 );
2657
2658 for i in 0..MAX_ON_SET_CALLBACKS {
2660 assert_eq!(
2661 server.apply(NODE, "rate", ParameterValue::Integer(40 + i as i64)),
2662 SetParameterResult::Rejected
2663 );
2664 }
2665 assert_eq!(
2666 server.apply(NODE, "rate", ParameterValue::Integer(7)),
2667 SetParameterResult::Success
2668 );
2669
2670 assert!(server.remove_on_set_parameters_callback(handles[0]));
2671 assert_eq!(server.on_set_callbacks_free(), 1);
2672 }
2673
2674 #[test]
2677 fn the_store_rules_run_before_the_hook() {
2678 let mut storage: ParameterStorage = ParameterStorage::new();
2679 let mut server = ParameterServer::new_in(storage.as_table());
2680 let desc = ParameterDescriptor::new("rate", ParameterType::Integer)
2681 .expect("name fits")
2682 .with_read_only(true);
2683 assert!(server.declare_with_descriptor(
2684 NODE,
2685 "rate",
2686 ParameterValue::Integer(1),
2687 Some(desc)
2688 ));
2689 let _ = server
2690 .add_on_set_parameters_callback(NODE, refuse_everything, OnSetContext::default())
2691 .expect("a free slot");
2692 assert_eq!(
2694 server.apply(NODE, "rate", ParameterValue::Integer(2)),
2695 SetParameterResult::ReadOnly
2696 );
2697 }
2698
2699 #[test]
2703 fn the_on_set_registry_costs_what_its_doc_says() {
2704 let slot = core::mem::size_of::<Option<OnSetCallbackSlot>>();
2705 let word = core::mem::size_of::<usize>();
2706 assert!(
2709 slot <= 4 * word,
2710 "an on-set slot is {slot} bytes, more than the four words its doc claims"
2711 );
2712 assert_eq!(
2713 core::mem::size_of::<[Option<OnSetCallbackSlot>; MAX_ON_SET_CALLBACKS]>(),
2714 MAX_ON_SET_CALLBACKS * slot
2715 );
2716 }
2717
2718 #[test]
2720 fn try_declare_names_the_refusal() {
2721 let mut storage: ParameterStorage<2> = ParameterStorage::new();
2722 let mut server = ParameterServer::new_in(storage.as_table());
2723
2724 assert_eq!(
2725 server.try_declare(NODE, "a", ParameterValue::Integer(1)),
2726 Ok(())
2727 );
2728 assert_eq!(
2729 server.try_declare(NODE, "a", ParameterValue::Integer(1)),
2730 Err(DeclarationError::AlreadyDeclared)
2731 );
2732
2733 let bad_range = ParameterDescriptor::new("b", ParameterType::Integer)
2734 .expect("name fits")
2735 .with_integer_range(10, 0, 1);
2736 assert_eq!(
2737 server.try_declare_with_descriptor(
2738 NODE,
2739 "b",
2740 ParameterValue::Integer(1),
2741 Some(bad_range)
2742 ),
2743 Err(DeclarationError::InvalidRange)
2744 );
2745
2746 let out_of_range = ParameterDescriptor::new("b", ParameterType::Integer)
2747 .expect("name fits")
2748 .with_integer_range(0, 10, 1);
2749 assert_eq!(
2750 server.try_declare_with_descriptor(
2751 NODE,
2752 "b",
2753 ParameterValue::Integer(99),
2754 Some(out_of_range)
2755 ),
2756 Err(DeclarationError::InitialValueOutOfRange)
2757 );
2758
2759 assert_eq!(
2760 server.try_declare(NODE, "b", ParameterValue::Integer(1)),
2761 Ok(())
2762 );
2763 assert_eq!(
2764 server.try_declare(NODE, "c", ParameterValue::Integer(1)),
2765 Err(DeclarationError::StorageFull)
2766 );
2767
2768 let long = "n".repeat(MAX_PARAM_NAME_LEN + 1);
2769 let mut roomy: ParameterStorage<2> = ParameterStorage::new();
2770 let mut server = ParameterServer::new_in(roomy.as_table());
2771 assert_eq!(
2772 server.try_declare(NODE, &long, ParameterValue::Integer(1)),
2773 Err(DeclarationError::NameTooLong)
2774 );
2775 }
2776}
2777
2778#[cfg(test)]
2783mod ghost_checks {
2784 use super::*;
2785 use nros_ghost_types::ParamServerGhost;
2786
2787 const NODE: NodeKey = NodeKey::PRIMARY;
2789
2790 fn ghost_from_server(s: &ParameterServer<'_>) -> ParamServerGhost {
2793 ParamServerGhost {
2794 count: s.count,
2795 max: s.table.capacity(),
2799 }
2800 }
2801
2802 #[test]
2803 fn ghost_new_count() {
2804 let mut storage: ParameterStorage = ParameterStorage::new();
2805 let capacity = storage.capacity();
2806 let server = ParameterServer::new_in(storage.as_table());
2807 let ghost = ghost_from_server(&server);
2808 assert_eq!(ghost.count, 0);
2809 assert_eq!(ghost.max, capacity);
2814 assert_eq!(ghost.max, MAX_PARAMETERS);
2815 }
2816
2817 #[test]
2820 fn ghost_max_follows_a_shorter_table() {
2821 let mut storage = ParameterStorage::<2>::new();
2822 let mut server = ParameterServer::new_in(storage.as_table());
2823 assert_eq!(ghost_from_server(&server).max, 2);
2824 assert!(server.declare(NODE, "a", ParameterValue::Integer(1)));
2825 assert!(server.declare(NODE, "b", ParameterValue::Integer(2)));
2826 assert!(server.is_full());
2827 assert!(!server.declare(NODE, "c", ParameterValue::Integer(3)));
2828 let ghost = ghost_from_server(&server);
2829 assert!(ghost.count <= ghost.max);
2830 }
2831
2832 #[test]
2836 fn ghost_count_recovers_from_a_reused_table() {
2837 let mut storage = ParameterStorage::<4>::new();
2838 {
2839 let mut first = ParameterServer::new_in(storage.as_table());
2840 assert!(first.declare(NODE, "kept", ParameterValue::Integer(7)));
2841 }
2842 let second = ParameterServer::new_in(storage.as_table());
2843 assert_eq!(ghost_from_server(&second).count, 1);
2844 assert_eq!(second.get_integer(NODE, "kept"), Some(7));
2845 }
2846
2847 #[test]
2848 fn ghost_declare_increments() {
2849 let mut storage: ParameterStorage = ParameterStorage::new();
2850 let mut server = ParameterServer::new_in(storage.as_table());
2851 let before = ghost_from_server(&server).count;
2852 assert!(server.declare(NODE, "test", ParameterValue::Integer(1)));
2853 let after = ghost_from_server(&server).count;
2854 assert_eq!(after, before + 1);
2855 }
2856
2857 #[test]
2858 fn ghost_remove_decrements() {
2859 let mut storage: ParameterStorage = ParameterStorage::new();
2860 let mut server = ParameterServer::new_in(storage.as_table());
2861 assert!(server.declare(NODE, "test", ParameterValue::Integer(1)));
2862 let before = ghost_from_server(&server).count;
2863 assert!(server.remove(NODE, "test"));
2864 let after = ghost_from_server(&server).count;
2865 assert_eq!(after, before - 1);
2866 }
2867
2868 #[test]
2869 fn ghost_count_bounded() {
2870 let mut storage: ParameterStorage = ParameterStorage::new();
2871 let mut server = ParameterServer::new_in(storage.as_table());
2872 for i in 0..MAX_PARAMETERS {
2873 let mut name = heapless::String::<64>::new();
2874 let _ = core::fmt::write(&mut name, format_args!("p{}", i));
2875 assert!(server.declare(NODE, name.as_str(), ParameterValue::Integer(i as i64)));
2876 }
2877 let ghost = ghost_from_server(&server);
2878 assert!(ghost.count <= ghost.max);
2879 assert_eq!(ghost.count, MAX_PARAMETERS);
2880 assert!(!server.declare(NODE, "overflow", ParameterValue::Integer(0)));
2882 let ghost2 = ghost_from_server(&server);
2883 assert!(ghost2.count <= ghost2.max);
2884 }
2885}
2886
2887#[cfg(kani)]
2892mod verification {
2893 use super::*;
2894
2895 const NODE: NodeKey = NodeKey::PRIMARY;
2897
2898 #[kani::proof]
2904 fn server_new_is_empty() {
2905 let mut storage: ParameterStorage = ParameterStorage::new();
2906 let server = ParameterServer::new_in(storage.as_table());
2907 assert!(server.is_empty(NODE));
2908 assert_eq!(server.len(NODE), 0);
2909 assert!(!server.is_full());
2910 }
2911
2912 #[kani::proof]
2913 fn server_declare_get_roundtrip_integer() {
2914 let mut storage: ParameterStorage = ParameterStorage::new();
2915 let mut server = ParameterServer::new_in(storage.as_table());
2916 let val: i64 = kani::any();
2917 assert!(server.declare(NODE, "test", ParameterValue::Integer(val)));
2918 assert_eq!(server.get_integer(NODE, "test"), Some(val));
2919 assert!(server.has(NODE, "test"));
2920 assert_eq!(server.len(NODE), 1);
2921 }
2922
2923 #[kani::proof]
2924 fn server_declare_get_roundtrip_bool() {
2925 let mut storage: ParameterStorage = ParameterStorage::new();
2926 let mut server = ParameterServer::new_in(storage.as_table());
2927 let val: bool = kani::any();
2928 assert!(server.declare(NODE, "test", ParameterValue::Bool(val)));
2929 assert_eq!(server.get_bool(NODE, "test"), Some(val));
2930 }
2931
2932 #[kani::proof]
2933 fn server_set_requires_declare() {
2934 let mut storage: ParameterStorage = ParameterStorage::new();
2935 let mut server = ParameterServer::new_in(storage.as_table());
2936 let val: i64 = kani::any();
2937 let result = server.set(NODE, "test", ParameterValue::Integer(val));
2939 assert_eq!(result, SetParameterResult::NotFound);
2940 }
2941
2942 #[kani::proof]
2943 fn server_duplicate_declare_fails() {
2944 let mut storage: ParameterStorage = ParameterStorage::new();
2945 let mut server = ParameterServer::new_in(storage.as_table());
2946 assert!(server.declare(NODE, "test", ParameterValue::Integer(1)));
2947 assert!(!server.declare(NODE, "test", ParameterValue::Integer(2)));
2948 assert_eq!(server.get_integer(NODE, "test"), Some(1));
2950 }
2951
2952 #[kani::proof]
2953 fn server_remove_clears() {
2954 let mut storage: ParameterStorage = ParameterStorage::new();
2955 let mut server = ParameterServer::new_in(storage.as_table());
2956 assert!(server.declare(NODE, "test", ParameterValue::Integer(42)));
2957 assert!(server.has(NODE, "test"));
2958 assert!(server.remove(NODE, "test"));
2959 assert!(!server.has(NODE, "test"));
2960 assert_eq!(server.len(NODE), 0);
2961 }
2962
2963 #[kani::proof]
2964 fn server_get_nonexistent_returns_none() {
2965 let mut storage: ParameterStorage = ParameterStorage::new();
2966 let server = ParameterServer::new_in(storage.as_table());
2967 assert!(server.get(NODE, "nonexistent").is_none());
2968 assert!(server.get_bool(NODE, "nonexistent").is_none());
2969 assert!(server.get_integer(NODE, "nonexistent").is_none());
2970 assert!(server.get_double(NODE, "nonexistent").is_none());
2971 assert!(server.get_string(NODE, "nonexistent").is_none());
2972 }
2973}