1use crate::types::{
7 MAX_PARAM_NAME_LEN, Parameter, ParameterDescriptor, ParameterType, ParameterValue,
8 SetParameterResult,
9};
10use heapless::String;
11
12use crate::types::MAX_PARAMETERS;
14
15#[derive(Debug, Clone)]
21pub(crate) struct ParameterEntry {
22 param: Parameter,
24 descriptor: Option<ParameterDescriptor>,
26}
27
28pub struct ParameterTable<'s> {
44 entries: &'s mut [Option<ParameterEntry>],
45}
46
47impl<'s> ParameterTable<'s> {
48 pub(crate) fn new(entries: &'s mut [Option<ParameterEntry>]) -> Self {
50 Self { entries }
51 }
52
53 pub fn capacity(&self) -> usize {
55 self.entries.len()
56 }
57
58 pub fn occupied(&self) -> usize {
60 self.entries.iter().filter(|slot| slot.is_some()).count()
61 }
62}
63
64impl core::fmt::Debug for ParameterTable<'_> {
65 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66 f.debug_struct("ParameterTable")
67 .field("capacity", &self.capacity())
68 .finish_non_exhaustive()
69 }
70}
71
72pub struct ParameterStorage<const N: usize = MAX_PARAMETERS> {
124 entries: [Option<ParameterEntry>; N],
125}
126
127impl<const N: usize> ParameterStorage<N> {
128 #[allow(clippy::new_without_default)]
137 #[allow(clippy::large_stack_arrays)] pub const fn new() -> Self {
139 Self {
140 entries: [const { None }; N],
141 }
142 }
143
144 pub const fn capacity(&self) -> usize {
146 N
147 }
148
149 pub fn as_table(&mut self) -> ParameterTable<'_> {
151 ParameterTable::new(&mut self.entries)
152 }
153
154 pub unsafe fn init_in_place(dst: *mut Self) {
171 unsafe {
175 let entries = core::ptr::addr_of_mut!((*dst).entries).cast::<Option<ParameterEntry>>();
176 for i in 0..N {
177 entries.add(i).write(None);
178 }
179 }
180 }
181}
182
183pub struct ParameterServer<'s> {
209 table: ParameterTable<'s>,
211 count: usize,
213}
214
215impl<'s> ParameterServer<'s> {
216 pub fn new_in(table: ParameterTable<'s>) -> Self {
227 let count = table.occupied();
228 Self { table, count }
229 }
230
231 pub fn len(&self) -> usize {
233 self.count
234 }
235
236 pub fn is_empty(&self) -> bool {
238 self.count == 0
239 }
240
241 pub fn capacity(&self) -> usize {
246 self.table.capacity()
247 }
248
249 pub fn is_full(&self) -> bool {
251 self.count >= self.table.capacity()
252 }
253
254 fn find_index(&self, name: &str) -> Option<usize> {
256 self.table.entries.iter().position(|entry| {
257 entry
258 .as_ref()
259 .map(|e| e.param.name.as_str() == name)
260 .unwrap_or(false)
261 })
262 }
263
264 fn find_empty_slot(&self) -> Option<usize> {
266 self.table.entries.iter().position(|entry| entry.is_none())
267 }
268
269 pub fn declare(&mut self, name: &str, value: ParameterValue) -> bool {
274 self.declare_with_descriptor(name, value, None)
275 }
276
277 pub fn declare_with_descriptor(
281 &mut self,
282 name: &str,
283 value: ParameterValue,
284 descriptor: Option<ParameterDescriptor>,
285 ) -> bool {
286 if self.find_index(name).is_some() {
288 return false;
289 }
290
291 let slot = match self.find_empty_slot() {
293 Some(idx) => idx,
294 None => return false,
295 };
296
297 let param = match Parameter::new(name, value) {
299 Some(p) => p,
300 None => return false,
301 };
302
303 self.table.entries[slot] = Some(ParameterEntry { param, descriptor });
304 self.count += 1;
305 true
306 }
307
308 pub fn get(&self, name: &str) -> Option<&ParameterValue> {
310 self.find_index(name)
311 .and_then(|idx| self.table.entries[idx].as_ref())
312 .map(|entry| &entry.param.value)
313 }
314
315 pub fn get_parameter(&self, name: &str) -> Option<&Parameter> {
317 self.find_index(name)
318 .and_then(|idx| self.table.entries[idx].as_ref())
319 .map(|entry| &entry.param)
320 }
321
322 pub fn get_descriptor(&self, name: &str) -> Option<&ParameterDescriptor> {
324 self.find_index(name)
325 .and_then(|idx| self.table.entries[idx].as_ref())
326 .and_then(|entry| entry.descriptor.as_ref())
327 }
328
329 pub fn set(&mut self, name: &str, value: ParameterValue) -> SetParameterResult {
333 let idx = match self.find_index(name) {
334 Some(idx) => idx,
335 None => return SetParameterResult::NotFound,
336 };
337
338 let entry = match self.table.entries[idx].as_mut() {
339 Some(e) => e,
340 None => return SetParameterResult::NotFound,
341 };
342
343 if let Some(ref desc) = entry.descriptor {
345 if desc.read_only {
346 return SetParameterResult::ReadOnly;
347 }
348
349 if !desc.dynamic_typing && desc.param_type != value.param_type() {
351 return SetParameterResult::TypeMismatch;
352 }
353
354 if !desc.validate_range(&value) {
356 return SetParameterResult::OutOfRange;
357 }
358 }
359
360 entry.param.value = value;
361 SetParameterResult::Success
362 }
363
364 pub fn unset(&mut self, name: &str) -> SetParameterResult {
369 let idx = match self.find_index(name) {
370 Some(idx) => idx,
371 None => return SetParameterResult::NotFound,
372 };
373
374 let entry = match self.table.entries[idx].as_mut() {
375 Some(e) => e,
376 None => return SetParameterResult::NotFound,
377 };
378
379 if let Some(ref desc) = entry.descriptor
381 && desc.read_only
382 {
383 return SetParameterResult::ReadOnly;
384 }
385
386 entry.param.value = ParameterValue::NotSet;
387 SetParameterResult::Success
388 }
389
390 pub fn set_or_declare(&mut self, name: &str, value: ParameterValue) -> SetParameterResult {
394 if self.find_index(name).is_some() {
395 self.set(name, value)
396 } else if self.declare(name, value) {
397 SetParameterResult::Success
398 } else {
399 SetParameterResult::StorageFull
400 }
401 }
402
403 pub fn has(&self, name: &str) -> bool {
405 self.find_index(name).is_some()
406 }
407
408 pub fn remove(&mut self, name: &str) -> bool {
412 if let Some(idx) = self.find_index(name) {
413 self.table.entries[idx] = None;
414 self.count -= 1;
415 true
416 } else {
417 false
418 }
419 }
420
421 pub fn get_type(&self, name: &str) -> Option<ParameterType> {
423 self.get(name).map(|v| v.param_type())
424 }
425
426 pub fn iter(&self) -> impl Iterator<Item = &Parameter> {
428 self.table
429 .entries
430 .iter()
431 .filter_map(|entry| entry.as_ref().map(|e| &e.param))
432 }
433
434 pub fn list_names(&self) -> impl Iterator<Item = &str> {
436 self.iter().map(|p| p.name.as_str())
437 }
438
439 pub fn list_with_prefix<'a>(&'a self, prefix: &'a str) -> impl Iterator<Item = &'a str> {
441 self.list_names()
442 .filter(move |name| name.starts_with(prefix))
443 }
444
445 pub fn get_bool(&self, name: &str) -> Option<bool> {
447 self.get(name).and_then(|v| v.as_bool())
448 }
449
450 pub fn get_integer(&self, name: &str) -> Option<i64> {
452 self.get(name).and_then(|v| v.as_integer())
453 }
454
455 pub fn get_double(&self, name: &str) -> Option<f64> {
457 self.get(name).and_then(|v| v.as_double())
458 }
459
460 pub fn get_string(&self, name: &str) -> Option<&str> {
462 self.get(name).and_then(|v| v.as_string())
463 }
464
465 pub fn set_bool(&mut self, name: &str, value: bool) -> SetParameterResult {
467 self.set(name, ParameterValue::Bool(value))
468 }
469
470 pub fn set_integer(&mut self, name: &str, value: i64) -> SetParameterResult {
472 self.set(name, ParameterValue::Integer(value))
473 }
474
475 pub fn set_double(&mut self, name: &str, value: f64) -> SetParameterResult {
477 self.set(name, ParameterValue::Double(value))
478 }
479
480 pub fn set_string(&mut self, name: &str, value: &str) -> SetParameterResult {
482 match ParameterValue::from_string(value) {
483 Some(v) => self.set(name, v),
484 None => SetParameterResult::StorageFull, }
486 }
487
488 pub fn declare_parameter(
497 &mut self,
498 descriptor: ParameterDescriptor,
499 initial_value: Option<&ParameterValue>,
500 ) -> Result<(), SetParameterResult> {
501 let name = descriptor.name.as_str();
502
503 if self.find_index(name).is_some() {
505 return Err(SetParameterResult::TypeMismatch); }
507
508 let slot = match self.find_empty_slot() {
510 Some(idx) => idx,
511 None => return Err(SetParameterResult::StorageFull),
512 };
513
514 let param_value = initial_value.cloned().unwrap_or_default();
515
516 let param = Parameter::new(name, param_value).ok_or(SetParameterResult::StorageFull)?; self.table.entries[slot] = Some(ParameterEntry {
519 param,
520 descriptor: Some(descriptor),
521 });
522 self.count += 1;
523 Ok(())
524 }
525
526 pub fn get_parameter_value(&self, name: &str) -> Option<ParameterValue> {
531 self.get(name).cloned()
532 }
533
534 pub fn set_parameter_value(&mut self, name: &str, value: ParameterValue) -> SetParameterResult {
539 self.set(name, value)
540 }
541}
542
543pub struct LegacyParameterBuilder<'a, 's> {
545 server: &'a mut ParameterServer<'s>,
546 name: String<MAX_PARAM_NAME_LEN>,
547 value: ParameterValue,
548 descriptor: Option<ParameterDescriptor>,
549}
550
551impl<'a, 's> LegacyParameterBuilder<'a, 's> {
552 pub fn new(
554 server: &'a mut ParameterServer<'s>,
555 name: &str,
556 value: ParameterValue,
557 ) -> Option<Self> {
558 let mut n = String::new();
559 n.push_str(name).ok()?;
560 Some(Self {
561 server,
562 name: n,
563 value,
564 descriptor: None,
565 })
566 }
567
568 pub fn description(mut self, desc: &str) -> Self {
570 let param_type = self.value.param_type();
571 if self.descriptor.is_none() {
572 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
573 }
574 if let Some(ref mut d) = self.descriptor {
575 d.description.clear();
576 let _ = d.description.push_str(desc);
577 }
578 self
579 }
580
581 pub fn read_only(mut self) -> Self {
583 let param_type = self.value.param_type();
584 if self.descriptor.is_none() {
585 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
586 }
587 if let Some(ref mut d) = self.descriptor {
588 d.read_only = true;
589 }
590 self
591 }
592
593 pub fn integer_range(mut self, min: i64, max: i64, step: i64) -> Self {
595 let param_type = self.value.param_type();
596 if self.descriptor.is_none() {
597 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
598 }
599 if let Some(ref mut d) = self.descriptor {
600 d.range = crate::types::ParameterRange::Integer(crate::types::IntegerRange::new(
601 min, max, step,
602 ));
603 }
604 self
605 }
606
607 pub fn float_range(mut self, min: f64, max: f64, step: f64) -> Self {
609 let param_type = self.value.param_type();
610 if self.descriptor.is_none() {
611 self.descriptor = ParameterDescriptor::new(self.name.as_str(), param_type);
612 }
613 if let Some(ref mut d) = self.descriptor {
614 d.range = crate::types::ParameterRange::FloatingPoint(
615 crate::types::FloatingPointRange::new(min, max, step),
616 );
617 }
618 self
619 }
620
621 pub fn declare(self) -> bool {
623 self.server
624 .declare_with_descriptor(self.name.as_str(), self.value, self.descriptor)
625 }
626}
627
628#[cfg(test)]
629mod tests {
630 use super::*;
631
632 #[test]
633 fn test_new_server() {
634 let mut storage: ParameterStorage = ParameterStorage::new();
635 let server = ParameterServer::new_in(storage.as_table());
636 assert_eq!(server.len(), 0);
637 assert!(server.is_empty());
638 }
639
640 #[test]
641 #[allow(clippy::approx_constant)]
642 fn test_declare_and_get() {
643 let mut storage: ParameterStorage = ParameterStorage::new();
644 let mut server = ParameterServer::new_in(storage.as_table());
645
646 assert!(server.declare("my_bool", ParameterValue::Bool(true)));
647 assert!(server.declare("my_int", ParameterValue::Integer(42)));
648 assert!(server.declare("my_double", ParameterValue::Double(3.14)));
649
650 assert_eq!(server.len(), 3);
651 assert!(!server.is_empty());
652
653 assert_eq!(server.get_bool("my_bool"), Some(true));
654 assert_eq!(server.get_integer("my_int"), Some(42));
655 assert_eq!(server.get_double("my_double"), Some(3.14));
656 }
657
658 #[test]
659 fn test_unset_clears_the_value_and_reports_success() {
660 let mut storage: ParameterStorage = ParameterStorage::new();
666 let mut server = ParameterServer::new_in(storage.as_table());
667 assert!(server.declare("count", ParameterValue::Integer(0)));
668 assert_eq!(server.set_integer("count", 10), SetParameterResult::Success);
669 assert_eq!(server.get_integer("count"), Some(10));
670
671 assert_eq!(server.unset("count"), SetParameterResult::Success);
672 assert_eq!(server.get_integer("count"), None);
673 }
674
675 #[test]
676 fn test_set_parameter() {
677 let mut storage: ParameterStorage = ParameterStorage::new();
678 let mut server = ParameterServer::new_in(storage.as_table());
679 server.declare("count", ParameterValue::Integer(0));
680
681 assert_eq!(server.set_integer("count", 10), SetParameterResult::Success);
682 assert_eq!(server.get_integer("count"), Some(10));
683 }
684
685 #[test]
686 fn test_set_nonexistent() {
687 let mut storage: ParameterStorage = ParameterStorage::new();
688 let mut server = ParameterServer::new_in(storage.as_table());
689 assert_eq!(
690 server.set("nonexistent", ParameterValue::Integer(1)),
691 SetParameterResult::NotFound
692 );
693 }
694
695 #[test]
696 fn test_read_only_parameter() {
697 let mut storage: ParameterStorage = ParameterStorage::new();
698 let mut server = ParameterServer::new_in(storage.as_table());
699
700 let desc = ParameterDescriptor::new("version", ParameterType::String)
701 .unwrap()
702 .with_read_only(true);
703
704 server.declare_with_descriptor(
705 "version",
706 ParameterValue::from_string("1.0.0").unwrap(),
707 Some(desc),
708 );
709
710 assert_eq!(
711 server.set_string("version", "2.0.0"),
712 SetParameterResult::ReadOnly
713 );
714 assert_eq!(server.get_string("version"), Some("1.0.0"));
715 }
716
717 #[test]
718 fn test_range_constraints() {
719 let mut storage: ParameterStorage = ParameterStorage::new();
720 let mut server = ParameterServer::new_in(storage.as_table());
721
722 let desc = ParameterDescriptor::new("speed", ParameterType::Double)
723 .unwrap()
724 .with_float_range(0.0, 10.0, 0.0);
725
726 server.declare_with_descriptor("speed", ParameterValue::Double(5.0), Some(desc));
727
728 assert_eq!(server.set_double("speed", 8.0), SetParameterResult::Success);
730
731 assert_eq!(
733 server.set_double("speed", 15.0),
734 SetParameterResult::OutOfRange
735 );
736 assert_eq!(server.get_double("speed"), Some(8.0)); }
738
739 #[test]
740 fn test_remove_parameter() {
741 let mut storage: ParameterStorage = ParameterStorage::new();
742 let mut server = ParameterServer::new_in(storage.as_table());
743 server.declare("temp", ParameterValue::Double(25.0));
744
745 assert!(server.has("temp"));
746 assert!(server.remove("temp"));
747 assert!(!server.has("temp"));
748 assert!(!server.remove("temp")); }
750
751 #[test]
752 fn test_list_parameters() {
753 let mut storage: ParameterStorage = ParameterStorage::new();
754 let mut server = ParameterServer::new_in(storage.as_table());
755 server.declare("robot.speed", ParameterValue::Double(1.0));
756 server.declare("robot.name", ParameterValue::from_string("bot1").unwrap());
757 server.declare("sensor.range", ParameterValue::Double(10.0));
758
759 let robot_params: heapless::Vec<&str, 8> = server.list_with_prefix("robot.").collect();
760 assert_eq!(robot_params.len(), 2);
761 }
762
763 #[test]
764 fn test_set_or_declare() {
765 let mut storage: ParameterStorage = ParameterStorage::new();
766 let mut server = ParameterServer::new_in(storage.as_table());
767
768 assert_eq!(
770 server.set_or_declare("new_param", ParameterValue::Integer(1)),
771 SetParameterResult::Success
772 );
773 assert_eq!(server.get_integer("new_param"), Some(1));
774
775 assert_eq!(
777 server.set_or_declare("new_param", ParameterValue::Integer(2)),
778 SetParameterResult::Success
779 );
780 assert_eq!(server.get_integer("new_param"), Some(2));
781 }
782
783 #[test]
784 fn test_duplicate_declare() {
785 let mut storage: ParameterStorage = ParameterStorage::new();
786 let mut server = ParameterServer::new_in(storage.as_table());
787 assert!(server.declare("param", ParameterValue::Integer(1)));
788 assert!(!server.declare("param", ParameterValue::Integer(2))); assert_eq!(server.get_integer("param"), Some(1)); }
791}
792
793#[cfg(test)]
798mod ghost_checks {
799 use super::*;
800 use nros_ghost_types::ParamServerGhost;
801
802 fn ghost_from_server(s: &ParameterServer<'_>) -> ParamServerGhost {
805 ParamServerGhost {
806 count: s.count,
807 max: s.table.capacity(),
811 }
812 }
813
814 #[test]
815 fn ghost_new_count() {
816 let mut storage: ParameterStorage = ParameterStorage::new();
817 let capacity = storage.capacity();
818 let server = ParameterServer::new_in(storage.as_table());
819 let ghost = ghost_from_server(&server);
820 assert_eq!(ghost.count, 0);
821 assert_eq!(ghost.max, capacity);
826 assert_eq!(ghost.max, MAX_PARAMETERS);
827 }
828
829 #[test]
832 fn ghost_max_follows_a_shorter_table() {
833 let mut storage = ParameterStorage::<2>::new();
834 let mut server = ParameterServer::new_in(storage.as_table());
835 assert_eq!(ghost_from_server(&server).max, 2);
836 assert!(server.declare("a", ParameterValue::Integer(1)));
837 assert!(server.declare("b", ParameterValue::Integer(2)));
838 assert!(server.is_full());
839 assert!(!server.declare("c", ParameterValue::Integer(3)));
840 let ghost = ghost_from_server(&server);
841 assert!(ghost.count <= ghost.max);
842 }
843
844 #[test]
848 fn ghost_count_recovers_from_a_reused_table() {
849 let mut storage = ParameterStorage::<4>::new();
850 {
851 let mut first = ParameterServer::new_in(storage.as_table());
852 assert!(first.declare("kept", ParameterValue::Integer(7)));
853 }
854 let second = ParameterServer::new_in(storage.as_table());
855 assert_eq!(ghost_from_server(&second).count, 1);
856 assert_eq!(second.get_integer("kept"), Some(7));
857 }
858
859 #[test]
860 fn ghost_declare_increments() {
861 let mut storage: ParameterStorage = ParameterStorage::new();
862 let mut server = ParameterServer::new_in(storage.as_table());
863 let before = ghost_from_server(&server).count;
864 assert!(server.declare("test", ParameterValue::Integer(1)));
865 let after = ghost_from_server(&server).count;
866 assert_eq!(after, before + 1);
867 }
868
869 #[test]
870 fn ghost_remove_decrements() {
871 let mut storage: ParameterStorage = ParameterStorage::new();
872 let mut server = ParameterServer::new_in(storage.as_table());
873 server.declare("test", ParameterValue::Integer(1));
874 let before = ghost_from_server(&server).count;
875 assert!(server.remove("test"));
876 let after = ghost_from_server(&server).count;
877 assert_eq!(after, before - 1);
878 }
879
880 #[test]
881 fn ghost_count_bounded() {
882 let mut storage: ParameterStorage = ParameterStorage::new();
883 let mut server = ParameterServer::new_in(storage.as_table());
884 for i in 0..MAX_PARAMETERS {
885 let mut name = heapless::String::<64>::new();
886 let _ = core::fmt::write(&mut name, format_args!("p{}", i));
887 server.declare(name.as_str(), ParameterValue::Integer(i as i64));
888 }
889 let ghost = ghost_from_server(&server);
890 assert!(ghost.count <= ghost.max);
891 assert_eq!(ghost.count, MAX_PARAMETERS);
892 assert!(!server.declare("overflow", ParameterValue::Integer(0)));
894 let ghost2 = ghost_from_server(&server);
895 assert!(ghost2.count <= ghost2.max);
896 }
897}
898
899#[cfg(kani)]
904mod verification {
905 use super::*;
906
907 #[kani::proof]
913 fn server_new_is_empty() {
914 let mut storage: ParameterStorage = ParameterStorage::new();
915 let server = ParameterServer::new_in(storage.as_table());
916 assert!(server.is_empty());
917 assert_eq!(server.len(), 0);
918 assert!(!server.is_full());
919 }
920
921 #[kani::proof]
922 fn server_declare_get_roundtrip_integer() {
923 let mut storage: ParameterStorage = ParameterStorage::new();
924 let mut server = ParameterServer::new_in(storage.as_table());
925 let val: i64 = kani::any();
926 assert!(server.declare("test", ParameterValue::Integer(val)));
927 assert_eq!(server.get_integer("test"), Some(val));
928 assert!(server.has("test"));
929 assert_eq!(server.len(), 1);
930 }
931
932 #[kani::proof]
933 fn server_declare_get_roundtrip_bool() {
934 let mut storage: ParameterStorage = ParameterStorage::new();
935 let mut server = ParameterServer::new_in(storage.as_table());
936 let val: bool = kani::any();
937 assert!(server.declare("test", ParameterValue::Bool(val)));
938 assert_eq!(server.get_bool("test"), Some(val));
939 }
940
941 #[kani::proof]
942 fn server_set_requires_declare() {
943 let mut storage: ParameterStorage = ParameterStorage::new();
944 let mut server = ParameterServer::new_in(storage.as_table());
945 let val: i64 = kani::any();
946 let result = server.set("test", ParameterValue::Integer(val));
948 assert_eq!(result, SetParameterResult::NotFound);
949 }
950
951 #[kani::proof]
952 fn server_duplicate_declare_fails() {
953 let mut storage: ParameterStorage = ParameterStorage::new();
954 let mut server = ParameterServer::new_in(storage.as_table());
955 assert!(server.declare("test", ParameterValue::Integer(1)));
956 assert!(!server.declare("test", ParameterValue::Integer(2)));
957 assert_eq!(server.get_integer("test"), Some(1));
959 }
960
961 #[kani::proof]
962 fn server_remove_clears() {
963 let mut storage: ParameterStorage = ParameterStorage::new();
964 let mut server = ParameterServer::new_in(storage.as_table());
965 assert!(server.declare("test", ParameterValue::Integer(42)));
966 assert!(server.has("test"));
967 assert!(server.remove("test"));
968 assert!(!server.has("test"));
969 assert_eq!(server.len(), 0);
970 }
971
972 #[kani::proof]
973 fn server_get_nonexistent_returns_none() {
974 let mut storage: ParameterStorage = ParameterStorage::new();
975 let server = ParameterServer::new_in(storage.as_table());
976 assert!(server.get("nonexistent").is_none());
977 assert!(server.get_bool("nonexistent").is_none());
978 assert!(server.get_integer("nonexistent").is_none());
979 assert!(server.get_double("nonexistent").is_none());
980 assert!(server.get_string("nonexistent").is_none());
981 }
982}