drogue_ajour_protocol/
lib.rs

1//! A protocol for updating firmware of embedded devices from a remote server. The protocol is not
2//! tied to any specific platform, but is designed to work with Drogue Ajour and Drogue Cloud.
3#![no_std]
4
5use core::cmp::Ordering;
6use core::hash::{Hash, Hasher};
7use core::ops::Deref;
8use serde::{de::Visitor, Deserialize, Serialize};
9
10#[derive(Serialize, Deserialize, Debug)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub struct Status<'a> {
13    #[serde(borrow)]
14    pub version: Bytes<'a>,
15    pub mtu: Option<u32>,
16    pub correlation_id: Option<u32>,
17    pub update: Option<UpdateStatus<'a>>,
18}
19
20#[derive(Serialize, Deserialize, Debug)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub struct UpdateStatus<'a> {
23    #[serde(borrow)]
24    pub version: Bytes<'a>,
25    pub offset: u32,
26}
27
28impl<'a> Status<'a> {
29    pub fn first(version: &'a [u8], mtu: Option<u32>, correlation_id: Option<u32>) -> Self {
30        Self {
31            version: Bytes::new(version),
32            mtu,
33            correlation_id,
34            update: None,
35        }
36    }
37
38    pub fn update(
39        version: &'a [u8],
40        mtu: Option<u32>,
41        offset: u32,
42        next_version: &'a [u8],
43        correlation_id: Option<u32>,
44    ) -> Self {
45        Self {
46            version: Bytes::new(version),
47            mtu,
48            correlation_id,
49            update: Some(UpdateStatus {
50                offset,
51                version: Bytes::new(next_version),
52            }),
53        }
54    }
55}
56#[derive(Serialize, Deserialize, Debug)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub enum Command<'a> {
59    Wait {
60        correlation_id: Option<u32>,
61        poll: Option<u32>,
62    },
63    Sync {
64        #[serde(borrow)]
65        version: Bytes<'a>,
66        correlation_id: Option<u32>,
67        poll: Option<u32>,
68    },
69    Write {
70        #[serde(borrow)]
71        version: Bytes<'a>,
72        correlation_id: Option<u32>,
73        offset: u32,
74        #[serde(borrow)]
75        data: Bytes<'a>,
76    },
77    Swap {
78        #[serde(borrow)]
79        version: Bytes<'a>,
80        correlation_id: Option<u32>,
81        #[serde(borrow)]
82        checksum: Bytes<'a>,
83    },
84}
85
86impl<'a> Command<'a> {
87    pub fn new_wait(poll: Option<u32>, correlation_id: Option<u32>) -> Self {
88        Self::Wait {
89            correlation_id,
90            poll,
91        }
92    }
93
94    pub fn new_sync(version: &'a [u8], poll: Option<u32>, correlation_id: Option<u32>) -> Self {
95        Self::Sync {
96            version: Bytes::new(version),
97            correlation_id,
98            poll,
99        }
100    }
101
102    pub fn new_swap(version: &'a [u8], checksum: &'a [u8], correlation_id: Option<u32>) -> Self {
103        Self::Swap {
104            version: Bytes::new(version),
105            correlation_id,
106            checksum: Bytes::new(checksum),
107        }
108    }
109
110    pub fn new_write(
111        version: &'a [u8],
112        offset: u32,
113        data: &'a [u8],
114        correlation_id: Option<u32>,
115    ) -> Self {
116        Self::Write {
117            version: Bytes::new(version),
118            correlation_id,
119            offset,
120            data: Bytes::new(data),
121        }
122    }
123}
124
125#[derive(Debug)]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127pub struct Bytes<'a> {
128    data: &'a [u8],
129}
130
131impl<'a> Bytes<'a> {
132    pub fn new(data: &'a [u8]) -> Self {
133        Self { data }
134    }
135}
136
137impl<'a> Serialize for Bytes<'a> {
138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139    where
140        S: serde::Serializer,
141    {
142        serializer.serialize_bytes(self.data)
143    }
144}
145
146impl<'a, 'de: 'a> Deserialize<'de> for Bytes<'a> {
147    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148    where
149        D: serde::Deserializer<'de>,
150    {
151        deserializer.deserialize_bytes(BytesVisitor)
152    }
153}
154
155impl<'a> AsRef<[u8]> for Bytes<'a> {
156    fn as_ref(&self) -> &[u8] {
157        &self.data
158    }
159}
160
161impl<'a> Deref for Bytes<'a> {
162    type Target = [u8];
163
164    fn deref(&self) -> &Self::Target {
165        &self.data
166    }
167}
168
169impl<'a> Default for Bytes<'a> {
170    fn default() -> Self {
171        Bytes::new(&[])
172    }
173}
174
175impl<'a, Rhs> PartialEq<Rhs> for Bytes<'a>
176where
177    Rhs: ?Sized + AsRef<[u8]>,
178{
179    fn eq(&self, other: &Rhs) -> bool {
180        self.as_ref().eq(other.as_ref())
181    }
182}
183
184impl<'a, Rhs> PartialOrd<Rhs> for Bytes<'a>
185where
186    Rhs: ?Sized + AsRef<[u8]>,
187{
188    fn partial_cmp(&self, other: &Rhs) -> Option<Ordering> {
189        self.as_ref().partial_cmp(other.as_ref())
190    }
191}
192
193impl<'a> Hash for Bytes<'a> {
194    fn hash<H: Hasher>(&self, state: &mut H) {
195        self.data.hash(state);
196    }
197}
198
199struct BytesVisitor;
200
201impl<'de> Visitor<'de> for BytesVisitor {
202    type Value = Bytes<'de>;
203
204    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
205        formatter.write_str("a byte slice")
206    }
207
208    fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
209    where
210        E: serde::de::Error,
211    {
212        Ok(Bytes::new(v))
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    extern crate std;
219    use super::*;
220    use std::println;
221    use std::vec::Vec;
222
223    #[test]
224    fn deserialize_ref() {
225        let s = Command::new_write(b"1234", 0, &[1, 2, 3, 4], None);
226        let out = serde_cbor::to_vec(&s).unwrap();
227
228        let s: Command = serde_cbor::from_slice(&out).unwrap();
229        println!("Out: {:?}", s);
230    }
231
232    #[test]
233    fn serialized_status_size() {
234        // 1 byte version, 4 byte payload, 4 byte checksum
235        let version = &[1];
236        let mtu = Some(4);
237        let cid = None;
238        let offset = 0;
239        let next_version = &[2];
240
241        let s = Status::first(version, mtu, cid);
242        let first = encode(&s);
243
244        let s = Status::update(version, mtu, offset, next_version, cid);
245        let update = encode(&s);
246        println!(
247            "Serialized size:\n FIRST:\t{}\nUPDATE:\t{}",
248            first.len(),
249            update.len(),
250        );
251    }
252
253    #[test]
254    fn serialized_command_size() {
255        // 1 byte version, 4 byte payload, 4 byte checksum
256        let version = &[1];
257        let payload = &[1, 2, 3, 4];
258        let checksum = &[1, 2, 3, 4];
259
260        let s = Command::new_write(version, 0, payload, None);
261        let write = encode(&s);
262
263        let s = Command::new_wait(Some(1), None);
264        let wait = encode(&s);
265
266        let s = Command::new_sync(version, Some(1), None);
267        let sync = encode(&s);
268
269        let s = Command::new_swap(version, checksum, None);
270        let swap = encode(&s);
271        println!(
272            "Serialized size:\n WRITE:\t{}\nWAIT:\t{}\nSYNC:\t{}\nSWAP:\t{}",
273            write.len(),
274            wait.len(),
275            sync.len(),
276            swap.len()
277        );
278    }
279
280    fn encode<T>(value: &T) -> Vec<u8>
281    where
282        T: serde::Serialize,
283    {
284        serde_cbor::ser::to_vec_packed(value).unwrap()
285    }
286}