1#[cfg(test)]
7use crate::ParameterStorage;
8use crate::{
9 DeclarationError, NodeKey, ParameterDescriptor, ParameterRange, ParameterServer, ParameterType,
10};
11use heapless::String;
12
13pub use crate::ParameterVariant;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ParameterError {
19 TypeMismatch,
21 OutOfRange,
23 ReadOnly,
25 NotFound,
27 StorageFull,
29 StringConversion,
31 InvalidRange,
33 Rejected,
35 Undeclared,
44}
45
46#[allow(deprecated)]
47impl From<crate::SetParameterResult> for ParameterError {
48 fn from(result: crate::SetParameterResult) -> Self {
51 use crate::SetParameterResult as R;
52 match result {
53 R::TypeMismatch => ParameterError::TypeMismatch,
54 R::OutOfRange => ParameterError::OutOfRange,
55 R::ReadOnly => ParameterError::ReadOnly,
56 R::NotFound => ParameterError::NotFound,
57 R::StorageFull => ParameterError::StorageFull,
58 R::InvalidRange => ParameterError::InvalidRange,
59 R::Rejected => ParameterError::Rejected,
60 R::Undeclared => ParameterError::Undeclared,
61 R::Success => panic!("SetParameterResult::Success is not an error"),
62 }
63 }
64}
65
66pub trait RangeConvertible: ParameterVariant + Clone {
68 fn to_parameter_range(
70 range: core::ops::RangeInclusive<Self>,
71 ) -> Result<ParameterRange, ParameterError>;
72}
73
74impl RangeConvertible for i64 {
75 fn to_parameter_range(
76 range: core::ops::RangeInclusive<Self>,
77 ) -> Result<ParameterRange, ParameterError> {
78 Ok(ParameterRange::Integer(crate::IntegerRange::new(
79 *range.start(),
80 *range.end(),
81 1, )))
83 }
84}
85
86impl RangeConvertible for f64 {
87 fn to_parameter_range(
88 range: core::ops::RangeInclusive<Self>,
89 ) -> Result<ParameterRange, ParameterError> {
90 Ok(ParameterRange::FloatingPoint(
91 crate::FloatingPointRange::new(
92 *range.start(),
93 *range.end(),
94 0.0, ),
96 ))
97 }
98}
99
100pub struct ParameterBuilder<'a, 's, T: ParameterVariant> {
102 server: &'a mut ParameterServer<'s>,
106 node: NodeKey,
110 name: &'a str,
112 default: Option<T>,
114 description: Option<&'a str>,
116 constraints: Option<&'a str>,
120 range: Option<ParameterRange>,
122 read_only: bool,
124 _phantom: core::marker::PhantomData<T>,
126}
127
128impl<'a, 's, T: ParameterVariant> ParameterBuilder<'a, 's, T> {
129 pub fn new(server: &'a mut ParameterServer<'s>, node: NodeKey, name: &'a str) -> Self {
131 Self {
132 server,
133 node,
134 name,
135 default: None,
136 description: None,
137 constraints: None,
138 range: None,
139 read_only: false,
140 _phantom: core::marker::PhantomData,
141 }
142 }
143
144 pub fn default(mut self, value: T) -> Self {
153 self.default = Some(value);
154 self
155 }
156
157 pub fn description(mut self, desc: &'a str) -> Self {
159 self.description = Some(desc);
160 self
161 }
162
163 pub fn constraints(mut self, text: &'a str) -> Self {
175 self.constraints = Some(text);
176 self
177 }
178
179 pub fn integer_range(mut self, min: i64, max: i64, step: i64) -> Result<Self, ParameterError> {
181 if T::parameter_type() != ParameterType::Integer {
182 return Err(ParameterError::InvalidRange);
183 }
184 let range = crate::IntegerRange::new(min, max, step);
185 if !range.is_valid() {
188 return Err(ParameterError::InvalidRange);
189 }
190 self.range = Some(ParameterRange::Integer(range));
191 Ok(self)
192 }
193
194 pub fn float_range(mut self, min: f64, max: f64, step: f64) -> Result<Self, ParameterError> {
196 if T::parameter_type() != ParameterType::Double {
197 return Err(ParameterError::InvalidRange);
198 }
199 let range = crate::FloatingPointRange::new(min, max, step);
200 if !range.is_valid() {
201 return Err(ParameterError::InvalidRange);
202 }
203 self.range = Some(ParameterRange::FloatingPoint(range));
204 Ok(self)
205 }
206
207 pub fn range(mut self, range: core::ops::RangeInclusive<T>) -> Result<Self, ParameterError>
215 where
216 T: RangeConvertible,
217 {
218 self.range = Some(T::to_parameter_range(range)?);
219 Ok(self)
220 }
221
222 fn build_descriptor(&self) -> Result<ParameterDescriptor, DeclarationError> {
237 let mut descriptor = ParameterDescriptor::new(self.name, T::parameter_type())
238 .ok_or(DeclarationError::NameTooLong)?;
239 if let Some(desc) = self.description {
240 descriptor.set_description(desc);
241 }
242 if let Some(text) = self.constraints {
243 descriptor.set_additional_constraints(text);
244 }
245 descriptor.range = self.range.unwrap_or_default();
246 Ok(descriptor)
247 }
248
249 pub fn read_only(self) -> Result<ReadOnlyParameter<'a, 's, T>, DeclarationError> {
254 let default_value = self
256 .default
257 .as_ref()
258 .ok_or(DeclarationError::NoValueAvailable)?
259 .clone();
260
261 let mut descriptor = self.build_descriptor()?;
262 descriptor.read_only = true;
263
264 let param_value = default_value.to_parameter_value();
265
266 declare(self.server, self.node, descriptor, Some(¶m_value))?;
267
268 Ok(ReadOnlyParameter::new(self.server, self.node, self.name))
269 }
270
271 pub fn mandatory(self) -> Result<MandatoryParameter<'a, 's, T>, DeclarationError> {
275 let mut descriptor = self.build_descriptor()?;
276 descriptor.read_only = self.read_only;
277
278 let default_value = self.default.map(|v| v.to_parameter_value());
279
280 declare(self.server, self.node, descriptor, default_value.as_ref())?;
281
282 Ok(MandatoryParameter::new(self.server, self.node, self.name))
283 }
284
285 pub fn optional(self) -> Result<OptionalParameter<'a, 's, T>, DeclarationError> {
287 let mut descriptor = self.build_descriptor()?;
288 descriptor.read_only = self.read_only;
289
290 let default_value = self.default.map(|v| v.to_parameter_value());
291
292 declare(self.server, self.node, descriptor, default_value.as_ref())?;
293
294 Ok(OptionalParameter::new(self.server, self.node, self.name))
295 }
296}
297
298fn declare(
307 server: &mut ParameterServer<'_>,
308 node: NodeKey,
309 descriptor: ParameterDescriptor,
310 value: Option<&crate::ParameterValue>,
311) -> Result<(), DeclarationError> {
312 let consistency = ParameterServer::declaration_consistency(
313 Some(&descriptor),
314 &value.cloned().unwrap_or_default(),
315 );
316 server
317 .declare_parameter(node, descriptor, value)
318 .map_err(|result| match result {
319 ParameterError::TypeMismatch => DeclarationError::AlreadyDeclared,
320 ParameterError::InvalidRange => {
321 consistency.err().unwrap_or(DeclarationError::InvalidRange)
322 }
323 _ => DeclarationError::StorageFull,
324 })
325}
326
327pub struct MandatoryParameter<'a, 's, T: ParameterVariant> {
329 server: &'a mut ParameterServer<'s>,
330 node: NodeKey,
332 name: String<{ crate::MAX_PARAM_NAME_LEN }>,
333 _phantom: core::marker::PhantomData<T>,
334}
335
336impl<'a, 's, T: ParameterVariant> MandatoryParameter<'a, 's, T> {
337 pub(crate) fn new(server: &'a mut ParameterServer<'s>, node: NodeKey, name: &'a str) -> Self {
338 let mut n = String::new();
339 n.push_str(name).unwrap();
340 Self {
341 server,
342 node,
343 name: n,
344 _phantom: core::marker::PhantomData,
345 }
346 }
347
348 pub fn get(&self) -> T {
350 self.server
351 .get_parameter_value(self.node, self.name.as_str())
352 .and_then(|val| T::from_parameter_value(&val))
353 .expect("Mandatory parameter must have a value")
354 }
355
356 pub fn set(&mut self, value: T) -> Result<(), ParameterError> {
358 let converted = value
362 .try_to_parameter_value()
363 .map_err(|_| ParameterError::StringConversion)?;
364 let result = self
365 .server
366 .set_parameter_value(self.node, self.name.as_str(), converted);
367 result.map_err(|e| match e {
370 ParameterError::Undeclared => ParameterError::NotFound,
371 e => e,
372 })
373 }
374}
375
376pub struct OptionalParameter<'a, 's, T: ParameterVariant> {
378 server: &'a mut ParameterServer<'s>,
379 node: NodeKey,
381 name: String<{ crate::MAX_PARAM_NAME_LEN }>,
382 _phantom: core::marker::PhantomData<T>,
383}
384
385impl<'a, 's, T: ParameterVariant> OptionalParameter<'a, 's, T> {
386 pub(crate) fn new(server: &'a mut ParameterServer<'s>, node: NodeKey, name: &'a str) -> Self {
387 let mut n = String::new();
388 n.push_str(name).unwrap();
389 Self {
390 server,
391 node,
392 name: n,
393 _phantom: core::marker::PhantomData,
394 }
395 }
396
397 pub fn get(&self) -> Option<T> {
399 self.server
400 .get_parameter_value(self.node, self.name.as_str())
401 .and_then(|val| T::from_parameter_value(&val))
402 }
403
404 pub fn set(&mut self, value: Option<T>) -> Result<(), ParameterError> {
406 let param_value = value.map(|v| v.to_parameter_value());
407 let result = self.server.set_parameter_value(
408 self.node,
409 self.name.as_str(),
410 param_value.unwrap_or_default(),
411 );
412 result.map_err(|e| match e {
415 ParameterError::Undeclared => ParameterError::NotFound,
416 e => e,
417 })
418 }
419}
420
421pub struct ReadOnlyParameter<'a, 's, T: ParameterVariant> {
423 server: &'a mut ParameterServer<'s>,
424 node: NodeKey,
426 name: String<{ crate::MAX_PARAM_NAME_LEN }>,
427 _phantom: core::marker::PhantomData<T>,
428}
429
430impl<'a, 's, T: ParameterVariant> ReadOnlyParameter<'a, 's, T> {
431 pub(crate) fn new(server: &'a mut ParameterServer<'s>, node: NodeKey, name: &'a str) -> Self {
432 let mut n = String::new();
433 n.push_str(name).unwrap();
434 Self {
435 server,
436 node,
437 name: n,
438 _phantom: core::marker::PhantomData,
439 }
440 }
441
442 pub fn get(&self) -> T {
444 self.server
445 .get_parameter_value(self.node, self.name.as_str())
446 .and_then(|val| T::from_parameter_value(&val))
447 .expect("Read-only parameter must have a value")
448 }
449}
450
451pub struct Parameters<'a, 's> {
459 server: &'a mut ParameterServer<'s>,
460 node: NodeKey,
463}
464
465#[deprecated(
467 since = "0.6.0",
468 note = "renamed to `Parameters`, rclrs's name (phase-515 R8)"
469)]
470pub type UndeclaredParameters<'a, 's> = Parameters<'a, 's>;
471
472impl<'a, 's> Parameters<'a, 's> {
473 pub fn new(server: &'a mut ParameterServer<'s>, node: NodeKey) -> Self {
474 Self { server, node }
475 }
476
477 pub fn get<T: crate::ParameterVariant>(&self, name: &str) -> Option<T> {
480 self.server
481 .get(self.node, name)
482 .and_then(|value| T::from_parameter_value(value))
483 }
484
485 pub fn set<T: crate::ParameterVariant>(
492 &mut self,
493 name: &str,
494 value: T,
495 ) -> Result<(), ParameterError> {
496 let value = value
497 .try_to_parameter_value()
498 .map_err(|_| ParameterError::StringConversion)?;
499 self.set_value(name, value)
500 }
501
502 pub fn get_bool(&self, name: &str) -> Option<bool> {
504 self.server.get_bool(self.node, name)
505 }
506
507 pub fn get_integer(&self, name: &str) -> Option<i64> {
509 self.server.get_integer(self.node, name)
510 }
511
512 pub fn get_double(&self, name: &str) -> Option<f64> {
514 self.server.get_double(self.node, name)
515 }
516
517 pub fn get_string(&self, name: &str) -> Option<&str> {
519 self.server.get_string(self.node, name)
520 }
521
522 pub fn set_bool(&mut self, name: &str, value: bool) -> Result<(), ParameterError> {
534 self.set_value(name, crate::ParameterValue::Bool(value))
535 }
536
537 pub fn set_integer(&mut self, name: &str, value: i64) -> Result<(), ParameterError> {
539 self.set_value(name, crate::ParameterValue::Integer(value))
540 }
541
542 pub fn set_double(&mut self, name: &str, value: f64) -> Result<(), ParameterError> {
544 self.set_value(name, crate::ParameterValue::Double(value))
545 }
546
547 pub fn set_string(&mut self, name: &str, value: &str) -> Result<(), ParameterError> {
549 let value =
550 crate::ParameterValue::from_string(value).ok_or(ParameterError::StringConversion)?;
551 self.set_value(name, value)
552 }
553
554 fn set_value(
555 &mut self,
556 name: &str,
557 value: crate::ParameterValue,
558 ) -> Result<(), ParameterError> {
559 self.server
560 .set_or_declare(self.node, name, value)
561 .map_err(|e| match e {
562 ParameterError::Undeclared => ParameterError::NotFound,
563 e => e,
564 })
565 }
566}
567
568#[cfg(test)]
569mod tests {
570 use super::*;
571
572 const NODE: NodeKey = NodeKey::PRIMARY;
574
575 #[test]
576 fn test_mandatory_parameter_with_default() {
577 let mut storage: ParameterStorage = ParameterStorage::new();
578 let mut server = ParameterServer::new_in(storage.as_table());
579 let param = ParameterBuilder::<i64>::new(&mut server, NODE, "test_param")
580 .default(42)
581 .description("A test parameter")
582 .mandatory()
583 .expect("Failed to declare parameter");
584
585 assert_eq!(param.get(), 42);
586 }
587
588 #[test]
589 fn test_mandatory_parameter_set() {
590 let mut storage: ParameterStorage = ParameterStorage::new();
591 let mut server = ParameterServer::new_in(storage.as_table());
592 let mut param = ParameterBuilder::<i64>::new(&mut server, NODE, "test_param")
593 .default(0)
594 .mandatory()
595 .expect("Failed to declare parameter");
596
597 param.set(100).expect("Failed to set parameter");
598 assert_eq!(param.get(), 100);
599 }
600
601 #[test]
602 fn test_optional_parameter_none() {
603 let mut storage: ParameterStorage = ParameterStorage::new();
604 let mut server = ParameterServer::new_in(storage.as_table());
605 let param = ParameterBuilder::<i64>::new(&mut server, NODE, "test_param")
606 .optional()
607 .expect("Failed to declare parameter");
608
609 assert_eq!(param.get(), None);
610 }
611
612 #[test]
613 fn test_optional_parameter_with_default() {
614 let mut storage: ParameterStorage = ParameterStorage::new();
615 let mut server = ParameterServer::new_in(storage.as_table());
616 let param = ParameterBuilder::<i64>::new(&mut server, NODE, "test_param")
617 .default(42)
618 .optional()
619 .expect("Failed to declare parameter");
620
621 assert_eq!(param.get(), Some(42));
622 }
623
624 #[test]
625 fn test_optional_parameter_set() {
626 let mut storage: ParameterStorage = ParameterStorage::new();
627 let mut server = ParameterServer::new_in(storage.as_table());
628 let mut param = ParameterBuilder::<i64>::new(&mut server, NODE, "test_param")
629 .optional()
630 .expect("Failed to declare parameter");
631
632 param.set(Some(100)).expect("Failed to set parameter");
633 assert_eq!(param.get(), Some(100));
634 }
635
636 #[test]
637 fn test_read_only_parameter() {
638 let mut storage: ParameterStorage = ParameterStorage::new();
639 let mut server = ParameterServer::new_in(storage.as_table());
640 let param = ParameterBuilder::<i64>::new(&mut server, NODE, "readonly_param")
641 .default(42)
642 .description("A read-only parameter")
643 .read_only()
644 .expect("Failed to declare parameter");
645
646 assert_eq!(param.get(), 42);
647 }
648
649 #[test]
650 fn test_read_only_parameter_requires_default() {
651 let mut storage: ParameterStorage = ParameterStorage::new();
652 let mut server = ParameterServer::new_in(storage.as_table());
653 let result = ParameterBuilder::<i64>::new(&mut server, NODE, "readonly_param").read_only();
654
655 assert_eq!(result.err(), Some(DeclarationError::NoValueAvailable));
656 }
657
658 #[test]
661 fn builder_terminals_report_declaration_errors() {
662 let mut storage: ParameterStorage = ParameterStorage::new();
663 let mut server = ParameterServer::new_in(storage.as_table());
664 ParameterBuilder::<i64>::new(&mut server, NODE, "dup")
665 .default(1)
666 .mandatory()
667 .expect("first declaration");
668 let again = ParameterBuilder::<i64>::new(&mut server, NODE, "dup")
669 .default(1)
670 .mandatory();
671 assert_eq!(again.err(), Some(DeclarationError::AlreadyDeclared));
672
673 let outside = ParameterBuilder::<i64>::new(&mut server, NODE, "ranged")
674 .default(500)
675 .integer_range(0, 100, 1)
676 .expect("well-formed range")
677 .optional();
678 assert_eq!(
679 outside.err(),
680 Some(DeclarationError::InitialValueOutOfRange)
681 );
682
683 let as_param_error: ParameterError = DeclarationError::AlreadyDeclared.into();
685 assert_eq!(as_param_error, ParameterError::TypeMismatch);
686 }
687
688 #[test]
689 fn test_integer_range_constraint() {
690 let mut storage: ParameterStorage = ParameterStorage::new();
691 let mut server = ParameterServer::new_in(storage.as_table());
692 let mut param = ParameterBuilder::<i64>::new(&mut server, NODE, "ranged_param")
693 .default(50)
694 .integer_range(0, 100, 1)
695 .expect("Failed to set range")
696 .mandatory()
697 .expect("Failed to declare parameter");
698
699 param.set(75).expect("Failed to set valid value");
701 assert_eq!(param.get(), 75);
702 }
703
704 #[test]
705 fn test_float_range_constraint() {
706 let mut storage: ParameterStorage = ParameterStorage::new();
707 let mut server = ParameterServer::new_in(storage.as_table());
708 let mut param = ParameterBuilder::<f64>::new(&mut server, NODE, "float_param")
709 .default(0.5)
710 .float_range(0.0, 1.0, 0.0)
711 .expect("Failed to set range")
712 .mandatory()
713 .expect("Failed to declare parameter");
714
715 param.set(0.75).expect("Failed to set valid value");
717 assert_eq!(param.get(), 0.75);
718 }
719
720 #[test]
721 fn test_range_convenience_integer() {
722 let mut storage: ParameterStorage = ParameterStorage::new();
723 let mut server = ParameterServer::new_in(storage.as_table());
724 let param = ParameterBuilder::<i64>::new(&mut server, NODE, "ranged_param")
725 .default(50)
726 .range(0..=100)
727 .expect("Failed to set range")
728 .mandatory()
729 .expect("Failed to declare parameter");
730
731 assert_eq!(param.get(), 50);
732 }
733
734 #[test]
735 fn test_range_convenience_float() {
736 let mut storage: ParameterStorage = ParameterStorage::new();
737 let mut server = ParameterServer::new_in(storage.as_table());
738 let param = ParameterBuilder::<f64>::new(&mut server, NODE, "float_param")
739 .default(0.5)
740 .range(0.0..=1.0)
741 .expect("Failed to set range")
742 .mandatory()
743 .expect("Failed to declare parameter");
744
745 assert_eq!(param.get(), 0.5);
746 }
747
748 #[allow(clippy::absurd_extreme_comparisons, clippy::unnecessary_min_or_max)]
759 #[test]
760 fn builder_constraints_reach_the_descriptor_from_every_terminal() {
761 let text = "positive, hz";
762 let fits = text.len() <= crate::MAX_PARAM_CONSTRAINTS_LEN;
763 let stored = &text[..crate::MAX_PARAM_CONSTRAINTS_LEN.min(text.len())];
764
765 for which in 0..3u8 {
766 let mut storage: ParameterStorage = ParameterStorage::new();
767 let mut server = ParameterServer::new_in(storage.as_table());
768 {
769 let b = ParameterBuilder::<f64>::new(&mut server, NODE, "rate")
770 .default(1.0)
771 .description("publish rate")
772 .constraints(text);
773 match which {
774 0 => {
775 b.read_only().expect("read_only");
776 }
777 1 => {
778 b.mandatory().expect("mandatory");
779 }
780 _ => {
781 b.optional().expect("optional");
782 }
783 }
784 }
785 let desc = server.get_descriptor(NODE, "rate").expect("descriptor");
786 assert_eq!(desc.description.as_str(), "publish rate");
787 assert_eq!(
788 desc.additional_constraints.as_str(),
789 stored,
790 "terminal {which} lost the constraints text"
791 );
792 let mut reported = 0usize;
793 server.take_truncated_descriptions(|_, _| reported += 1);
794 assert_eq!(
795 reported,
796 usize::from(!fits),
797 "terminal {which}: a constraints string that did not fit went unreported"
798 );
799 }
800 }
801
802 #[test]
803 fn test_parameter_description() {
804 let mut storage: ParameterStorage = ParameterStorage::new();
805 let mut server = ParameterServer::new_in(storage.as_table());
806 let _param = ParameterBuilder::<i64>::new(&mut server, NODE, "described_param")
807 .default(42)
808 .description("This is a test description")
809 .mandatory()
810 .expect("Failed to declare parameter");
811
812 let desc = server.get_descriptor(NODE, "described_param");
814 assert!(desc.is_some());
815 assert_eq!(
816 desc.unwrap().description.as_str(),
817 "This is a test description"
818 );
819 }
820
821 #[test]
822 fn test_bool_parameter() {
823 let mut storage: ParameterStorage = ParameterStorage::new();
824 let mut server = ParameterServer::new_in(storage.as_table());
825 let mut param = ParameterBuilder::<bool>::new(&mut server, NODE, "bool_param")
826 .default(false)
827 .mandatory()
828 .expect("Failed to declare parameter");
829
830 assert!(!param.get());
831 param.set(true).expect("Failed to set parameter");
832 assert!(param.get());
833 }
834
835 #[test]
836 fn test_undeclared_parameters() {
837 use crate::ParameterValue;
838
839 let mut storage: ParameterStorage = ParameterStorage::new();
840 let mut server = ParameterServer::new_in(storage.as_table());
841
842 server
844 .set_or_declare(NODE, "flag", ParameterValue::Bool(true))
845 .unwrap();
846 server
847 .set_or_declare(NODE, "count", ParameterValue::Integer(42))
848 .unwrap();
849 server
850 .set_or_declare(NODE, "ratio", ParameterValue::Double(0.5))
851 .unwrap();
852
853 let undeclared = Parameters::new(&mut server, NODE);
854
855 assert_eq!(undeclared.get_bool("flag"), Some(true));
856 assert_eq!(undeclared.get_integer("count"), Some(42));
857 assert_eq!(undeclared.get_double("ratio"), Some(0.5));
858 assert_eq!(undeclared.get_string("nonexistent"), None);
859 }
860
861 #[test]
863 fn parameters_generic_get_and_set() {
864 let mut storage: ParameterStorage = ParameterStorage::new();
865 let mut server = ParameterServer::new_in(storage.as_table());
866 let mut params = Parameters::new(&mut server, NODE);
867
868 params
869 .set("rate", 10i64)
870 .expect("set declares an absent name");
871 params.set("enabled", true).expect("set a bool");
872 assert_eq!(params.get::<i64>("rate"), Some(10));
873 assert_eq!(params.get::<bool>("enabled"), Some(true));
874 assert_eq!(params.get::<bool>("rate"), None);
876 assert_eq!(params.get::<i64>("missing"), None);
877 params.set("rate", 20i64).expect("overwrite");
878 assert_eq!(params.get::<i64>("rate"), Some(20));
879 }
880}