orchestra_toolkit/hgtp/message/
unpacker.rs

1/* Copyright 2024-2025 LEDR Technologies Inc.
2* This file is part of the Orchestra library, which helps developer use our Orchestra technology which is based on AvesTerra, owned and developped by Georgetown University, under license agreement with LEDR Technologies Inc.
3*
4* The Orchestra library is a free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
5*
6* The Orchestra library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
7*
8* You should have received a copy of the GNU Lesser General Public License along with the Orchestra library. If not, see <https://www.gnu.org/licenses/>.
9*
10* If you have any questions, feedback or issues about the Orchestra library, you can contact us at support@ledr.io.
11*/
12
13use std::string::FromUtf8Error;
14
15use crate::{taxonomy::*, Entity, String255, Token, Value, ValueCreationError};
16use thiserror::Error;
17use time::OffsetDateTime;
18
19use super::HGTPMessage;
20
21#[derive(Error, Debug)]
22pub enum UnpackError {
23    #[error("invalid command {0}")]
24    Command(u16),
25    #[error("invalid error code {0}: {1}")]
26    Error(u16, String),
27    #[error("invalid report {0}")]
28    Report(u16),
29    #[error("invalid method {0}")]
30    Method(u16),
31    #[error("invalid attribute {0}")]
32    Attribute(u16),
33    #[error("invalid event {0}")]
34    Event(u16),
35    #[error("invalid mode {0}")]
36    Mode(u16),
37    #[error("invalid category {0}")]
38    Category(u16),
39    #[error("invalid class {0}")]
40    Class(u16),
41    #[error("invalid context {0}")]
42    Context(u16),
43    #[error("invalid aspect {0}")]
44    Aspect(u16),
45    #[error("invalid state {0}")]
46    State(u16),
47    #[error("invalid condition {0}")]
48    Condition(u16),
49    #[error("invalid time: {0}")]
50    Time(time::error::ComponentRange),
51    #[error("invalid name: {0}")]
52    Name(FromUtf8Error),
53    #[error("invalid key: {0}")]
54    Key(FromUtf8Error),
55    #[error("invalid value: {0}")]
56    Value(#[from] UnpackValueError),
57}
58
59#[derive(Error, Debug)]
60#[error("invalid tag {0}")]
61pub struct UnpackTagError(pub u16);
62
63#[derive(Error, Debug)]
64pub enum UnpackValueError {
65    #[error(transparent)]
66    UnpackTag(#[from] UnpackTagError),
67    #[error("invalid bytes: {0}")]
68    InvalidBytes(FromUtf8Error),
69    #[error(transparent)]
70    ValueCreationError(#[from] ValueCreationError),
71}
72
73impl HGTPMessage {
74    pub fn unpack_command(&self) -> Result<Command, UnpackError> {
75        let raw = u16::from_le_bytes(self.frame[0..2].try_into().unwrap());
76
77        Command::try_from(raw).map_err(|_| UnpackError::Command(raw))
78    }
79
80    // Consider using `unpack_error`
81    pub fn unpack_error_code(&self) -> Result<HGTPError, UnpackError> {
82        let raw = u16::from_le_bytes(self.frame[2..4].try_into().unwrap());
83
84        HGTPError::try_from(raw).map_err(|_| {
85            UnpackError::Error(
86                raw,
87                String::from_utf8_lossy(self.unpack_bytes()).to_string(),
88            )
89        })
90    }
91
92    pub fn unpack_version(&self) -> u16 {
93        u16::from_le_bytes(self.frame[4..6].try_into().unwrap())
94    }
95
96    pub fn unpack_report(&self) -> Result<Report, UnpackError> {
97        let raw = u16::from_le_bytes(self.frame[6..8].try_into().unwrap());
98
99        Report::try_from(raw).map_err(|_| UnpackError::Report(raw))
100    }
101
102    pub fn unpack_method(&self) -> Result<Method, UnpackError> {
103        let v = u16::from_le_bytes(self.frame[8..10].try_into().unwrap());
104        Method::try_from(v).map_err(|_| UnpackError::Method(v))
105    }
106
107    pub fn unpack_attribute(&self) -> Result<Attribute, UnpackError> {
108        let v = u16::from_le_bytes(self.frame[10..12].try_into().unwrap());
109        Attribute::try_from(v).map_err(|_| UnpackError::Attribute(v))
110    }
111
112    pub fn unpack_event(&self) -> Result<Event, UnpackError> {
113        let v = u16::from_le_bytes(self.frame[12..14].try_into().unwrap());
114        Event::try_from(v).map_err(|_| UnpackError::Event(v))
115    }
116
117    pub fn unpack_mode(&self) -> Result<Mode, UnpackError> {
118        let v = u16::from_le_bytes(self.frame[14..16].try_into().unwrap());
119        Mode::try_from(v).map_err(|_| UnpackError::Mode(v))
120    }
121
122    pub fn unpack_category(&self) -> Result<Category, UnpackError> {
123        let v = u16::from_le_bytes(self.frame[16..18].try_into().unwrap());
124        Category::try_from(v).map_err(|_| UnpackError::Category(v))
125    }
126
127    pub fn unpack_class(&self) -> Result<Class, UnpackError> {
128        let v = u16::from_le_bytes(self.frame[18..20].try_into().unwrap());
129        Class::try_from(v).map_err(|_| UnpackError::Class(v))
130    }
131
132    pub fn unpack_context(&self) -> Result<Context, UnpackError> {
133        let v = u16::from_le_bytes(self.frame[20..22].try_into().unwrap());
134        Context::try_from(v).map_err(|_| UnpackError::Context(v))
135    }
136
137    pub fn unpack_aspect(&self) -> Result<Aspect, UnpackError> {
138        let v = u16::from_le_bytes(self.frame[22..24].try_into().unwrap());
139        Aspect::try_from(v).map_err(|_| UnpackError::Aspect(v))
140    }
141
142    pub fn unpack_state(&self) -> Result<State, UnpackError> {
143        let v = u16::from_le_bytes(self.frame[24..26].try_into().unwrap());
144        State::try_from(v).map_err(|_| UnpackError::State(v))
145    }
146
147    pub fn unpack_precedence(&self) -> u16 {
148        u16::from_le_bytes(self.frame[26..28].try_into().unwrap())
149    }
150
151    // You probably want to use `unpack_value`
152    pub fn unpack_tag(&self) -> Result<Tag, UnpackTagError> {
153        let v = u16::from_le_bytes(self.frame[28..30].try_into().unwrap());
154        Tag::try_from(v).map_err(|_| UnpackTagError(v))
155    }
156
157    pub fn unpack_condition(&self) -> Result<Condition, UnpackError> {
158        let v = u16::from_le_bytes(self.frame[30..32].try_into().unwrap());
159        Condition::try_from(v).map_err(|_| UnpackError::Condition(v))
160    }
161
162    pub fn unpack_instance(&self) -> i32 {
163        i32::from_le_bytes(self.frame[32..36].try_into().unwrap())
164    }
165
166    pub fn unpack_offset(&self) -> i32 {
167        i32::from_le_bytes(self.frame[36..40].try_into().unwrap())
168    }
169
170    pub fn unpack_time(&self) -> Result<OffsetDateTime, UnpackError> {
171        let timestamp = i64::from_le_bytes(self.frame[40..48].try_into().unwrap());
172
173        OffsetDateTime::from_unix_timestamp(timestamp).map_err(|e| UnpackError::Time(e))
174    }
175
176    pub fn unpack_index(&self) -> i64 {
177        i64::from_le_bytes(self.frame[48..56].try_into().unwrap())
178    }
179
180    pub fn unpack_count(&self) -> i64 {
181        i64::from_le_bytes(self.frame[56..64].try_into().unwrap())
182    }
183
184    pub fn unpack_extension(&self) -> i64 {
185        i64::from_le_bytes(self.frame[64..72].try_into().unwrap())
186    }
187
188    pub fn unpack_parameter(&self) -> i64 {
189        i64::from_le_bytes(self.frame[72..80].try_into().unwrap())
190    }
191
192    pub fn unpack_resultant(&self) -> i64 {
193        i64::from_le_bytes(self.frame[80..88].try_into().unwrap())
194    }
195
196    pub fn unpack_timeout(&self) -> i64 {
197        i64::from_le_bytes(self.frame[88..96].try_into().unwrap())
198    }
199
200    // -- 64 reserved bytes --
201
202    pub fn unpack_entity(&self) -> Entity {
203        Entity {
204            pid: u32::from_le_bytes(self.frame[160..164].try_into().unwrap()),
205            hid: u32::from_le_bytes(self.frame[164..168].try_into().unwrap()),
206            uid: u64::from_le_bytes(self.frame[168..176].try_into().unwrap()),
207        }
208    }
209
210    pub fn unpack_outlet(&self) -> Entity {
211        Entity {
212            pid: u32::from_le_bytes(self.frame[176..180].try_into().unwrap()),
213            hid: u32::from_le_bytes(self.frame[180..184].try_into().unwrap()),
214            uid: u64::from_le_bytes(self.frame[184..192].try_into().unwrap()),
215        }
216    }
217
218    pub fn unpack_auxiliary(&self) -> Entity {
219        Entity {
220            pid: u32::from_le_bytes(self.frame[192..196].try_into().unwrap()),
221            hid: u32::from_le_bytes(self.frame[196..200].try_into().unwrap()),
222            uid: u64::from_le_bytes(self.frame[200..208].try_into().unwrap()),
223        }
224    }
225
226    pub fn unpack_ancillary(&self) -> Entity {
227        Entity {
228            pid: u32::from_le_bytes(self.frame[208..212].try_into().unwrap()),
229            hid: u32::from_le_bytes(self.frame[212..216].try_into().unwrap()),
230            uid: u64::from_le_bytes(self.frame[216..224].try_into().unwrap()),
231        }
232    }
233
234    pub fn unpack_authorization(&self) -> Token {
235        Token::from_bytes(self.frame[224..240].try_into().unwrap())
236    }
237
238    pub fn unpack_authority(&self) -> Token {
239        Token::from_bytes(self.frame[240..256].try_into().unwrap())
240    }
241
242    pub fn unpack_name(&self) -> Result<String255, UnpackError> {
243        let len = self.frame[256];
244        let bytes = &self.frame[257..257 + len as usize];
245        match String::from_utf8(bytes.to_vec()) {
246            Ok(s) => Ok(String255::unchecked(&s)),
247            Err(e) => Err(UnpackError::Name(e)),
248        }
249    }
250
251    pub fn unpack_key(&self) -> Result<String255, UnpackError> {
252        let len = self.frame[512];
253        let bytes = &self.frame[513..513 + len as usize];
254        match String::from_utf8(bytes.to_vec()) {
255            Ok(s) => Ok(String255::unchecked(&s)),
256            Err(e) => Err(UnpackError::Key(e)),
257        }
258    }
259
260    // You probably want to use `unpack_value`
261    pub fn unpack_bytes(&self) -> &[u8] {
262        let extension = self.unpack_extension();
263        if extension == 0 {
264            let len = self.frame[768] as usize;
265            &self.frame[769..769 + len]
266        } else {
267            &self.unbounded
268        }
269    }
270
271    pub fn unpack_value(&self) -> Result<Value, UnpackError> {
272        let t = self.unpack_tag().map_err(UnpackValueError::UnpackTag)?;
273        let b = self.unpack_bytes().to_vec();
274        let s = String::from_utf8(b).map_err(UnpackValueError::InvalidBytes)?;
275        Ok(Value::new(t, s).map_err(UnpackValueError::ValueCreationError)?)
276    }
277}