umshctl/command/info/
props.rs1use std::collections::HashMap;
9
10use umsh::ulcp::{FrameLink, UlcpDevice};
11use umsh::ulcp_wire::Status;
12use umsh::ulcp_wire::ids::cap;
13
14#[derive(Debug, Default)]
26pub struct PropSet {
27 values: HashMap<u32, Result<Vec<u8>, Status>>,
28}
29
30impl PropSet {
31 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn insert(&mut self, key: u32, answer: Result<Vec<u8>, Status>) {
36 self.values.insert(key, answer);
37 }
38
39 pub fn answered(&self, key: u32) -> bool {
41 matches!(self.values.get(&key), Some(Ok(_)))
42 }
43
44 pub fn bytes(&self, key: u32) -> Option<&[u8]> {
46 match self.values.get(&key) {
47 Some(Ok(value)) => Some(value),
48 _ => None,
49 }
50 }
51
52 pub fn non_empty(&self, key: u32) -> Option<&[u8]> {
57 self.bytes(key).filter(|value| !value.is_empty())
58 }
59
60 pub fn refusal(&self, key: u32) -> Option<Status> {
62 match self.values.get(&key) {
63 Some(Err(status)) => Some(*status),
64 _ => None,
65 }
66 }
67
68 pub fn bool(&self, key: u32) -> Option<bool> {
69 self.bytes(key)?.first().map(|&byte| byte != 0)
70 }
71
72 pub fn u8(&self, key: u32) -> Option<u8> {
73 self.bytes(key)?.first().copied()
74 }
75
76 pub fn i8(&self, key: u32) -> Option<i8> {
77 self.u8(key).map(|byte| byte as i8)
78 }
79
80 pub fn u16(&self, key: u32) -> Option<u16> {
81 <[u8; 2]>::try_from(self.bytes(key)?)
82 .ok()
83 .map(u16::from_le_bytes)
84 }
85
86 pub fn i16(&self, key: u32) -> Option<i16> {
87 <[u8; 2]>::try_from(self.bytes(key)?)
88 .ok()
89 .map(i16::from_le_bytes)
90 }
91
92 pub fn u32(&self, key: u32) -> Option<u32> {
93 <[u8; 4]>::try_from(self.bytes(key)?)
94 .ok()
95 .map(u32::from_le_bytes)
96 }
97
98 pub fn i32(&self, key: u32) -> Option<i32> {
99 <[u8; 4]>::try_from(self.bytes(key)?)
100 .ok()
101 .map(i32::from_le_bytes)
102 }
103
104 pub fn text(&self, key: u32) -> Option<String> {
106 let value = self.bytes(key)?;
107 Some(
108 String::from_utf8_lossy(value)
109 .trim_end_matches('\0')
110 .to_owned(),
111 )
112 }
113
114 pub fn key32(&self, key: u32) -> Option<[u8; 32]> {
115 <[u8; 32]>::try_from(self.bytes(key)?).ok()
116 }
117}
118
119pub async fn fetch<L: FrameLink>(
130 device: &mut UlcpDevice<L>,
131 keys: &[u32],
132 batched: bool,
133) -> anyhow::Result<PropSet> {
134 let mut set = PropSet::new();
135 if keys.is_empty() {
136 return Ok(set);
137 }
138 if batched {
139 let answers = device.read_each(keys).await?;
140 for (&key, answer) in keys.iter().zip(answers) {
141 set.insert(key, answer);
142 }
143 return Ok(set);
144 }
145 for &key in keys {
146 let answer = match device.get_prop(key).await {
147 Ok(value) => Ok(value),
148 Err(umsh::ulcp::UlcpError::Status(status)) => Err(status),
152 Err(error) => return Err(error.into()),
153 };
154 set.insert(key, answer);
155 }
156 Ok(set)
157}
158
159pub fn batched(caps: &[u32]) -> bool {
161 caps.contains(&cap::CMD_MULTI)
162}