rmux_core/input/
params.rs1use super::PARAM_LIST_MAX;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum ParamType {
8 Missing,
10 Number(i32),
12 Str(String),
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct InputParam {
19 pub ptype: ParamType,
21}
22
23impl InputParam {
24 fn missing() -> Self {
25 Self {
26 ptype: ParamType::Missing,
27 }
28 }
29}
30
31pub(crate) struct ParamList {
33 params: [Option<InputParam>; PARAM_LIST_MAX],
34 len: u32,
35}
36
37impl ParamList {
38 pub(crate) fn new() -> Self {
39 Self {
40 params: std::array::from_fn(|_| None),
41 len: 0,
42 }
43 }
44
45 pub(crate) fn len(&self) -> u32 {
46 self.len
47 }
48
49 pub(crate) fn clear(&mut self) {
50 for p in &mut self.params {
51 *p = None;
52 }
53 self.len = 0;
54 }
55
56 pub(crate) fn split(&mut self, buf: &[u8], len: usize) -> bool {
59 self.clear();
60
61 if len == 0 {
62 return true;
63 }
64
65 let s = match std::str::from_utf8(&buf[..len]) {
66 Ok(s) => s,
67 Err(_) => return false,
68 };
69
70 for part in s.split(';') {
71 if self.len as usize >= PARAM_LIST_MAX {
72 return false;
73 }
74 let param = if part.is_empty() {
75 InputParam::missing()
76 } else if part.contains(':') {
77 InputParam {
78 ptype: ParamType::Str(part.to_owned()),
79 }
80 } else {
81 match part.parse::<i32>() {
82 Ok(n) if n >= 0 => InputParam {
83 ptype: ParamType::Number(n),
84 },
85 _ => return false,
86 }
87 };
88 self.params[self.len as usize] = Some(param);
89 self.len += 1;
90 }
91
92 true
93 }
94
95 pub(crate) fn get(&self, index: u32, minval: i32, defval: i32) -> i32 {
102 if index >= self.len {
103 return defval;
104 }
105 match &self.params[index as usize] {
106 None => defval,
107 Some(p) => match &p.ptype {
108 ParamType::Missing => defval,
109 ParamType::Str(_) => -1,
110 ParamType::Number(n) => {
111 if *n < minval {
112 minval
113 } else {
114 *n
115 }
116 }
117 },
118 }
119 }
120
121 pub(crate) fn param_at(&self, index: u32) -> Option<&InputParam> {
123 if index >= self.len {
124 return None;
125 }
126 self.params[index as usize].as_ref()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn split_empty() {
136 let mut pl = ParamList::new();
137 assert!(pl.split(b"", 0));
138 assert_eq!(pl.len(), 0);
139 assert_eq!(pl.get(0, 0, 42), 42);
140 }
141
142 #[test]
143 fn split_single_number() {
144 let mut pl = ParamList::new();
145 assert!(pl.split(b"5", 1));
146 assert_eq!(pl.len(), 1);
147 assert_eq!(pl.get(0, 0, 0), 5);
148 }
149
150 #[test]
151 fn split_multiple_with_missing() {
152 let mut pl = ParamList::new();
153 let buf = b";3;;7";
154 assert!(pl.split(buf, buf.len()));
155 assert_eq!(pl.len(), 4);
156 assert_eq!(pl.get(0, 0, 99), 99); assert_eq!(pl.get(1, 0, 0), 3);
158 assert_eq!(pl.get(2, 0, 99), 99); assert_eq!(pl.get(3, 0, 0), 7);
160 }
161
162 #[test]
163 fn split_colon_string() {
164 let mut pl = ParamList::new();
165 let buf = b"38:2:255:0:128";
166 assert!(pl.split(buf, buf.len()));
167 assert_eq!(pl.len(), 1);
168 assert_eq!(pl.get(0, 0, 0), -1); }
170
171 #[test]
172 fn get_clamps_to_minval() {
173 let mut pl = ParamList::new();
174 assert!(pl.split(b"0", 1));
175 assert_eq!(pl.get(0, 1, 1), 1);
176 }
177
178 #[test]
179 fn get_out_of_range_returns_defval() {
180 let mut pl = ParamList::new();
181 assert!(pl.split(b"5", 1));
182 assert_eq!(pl.get(5, 0, 42), 42);
183 }
184}