1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6use core::cmp::min;
7
8#[cfg(feature = "std")]
9use std::fmt::{Debug, Formatter};
10
11use crate::consts::PAYLOAD_MAX_SIZE;
12use crate::error::SpecError;
13use crate::types::{MavLinkVersion, MessageId};
14
15#[cfg(feature = "alloc")]
16type PayloadContainer = alloc::vec::Vec<u8>;
17#[cfg(not(feature = "alloc"))]
18use no_alloc_payload_container::PayloadContainer;
19
20#[derive(Clone)]
26#[cfg_attr(feature = "specta", derive(specta::Type))]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(not(feature = "std"), derive(Debug))]
29pub struct Payload {
30    id: MessageId,
32    payload: PayloadContainer,
34    length: usize,
36    version: MavLinkVersion,
38}
39
40#[allow(clippy::derivable_impls)]
41impl Default for Payload {
42    fn default() -> Self {
44        Self {
45            id: MessageId::default(),
46            payload: Payload::container_default(),
47            version: MavLinkVersion::default(),
48            length: PAYLOAD_MAX_SIZE,
49        }
50    }
51}
52
53#[cfg(feature = "std")]
54impl Debug for Payload {
55    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60        let payload = match self.version {
61            MavLinkVersion::V1 => &self.payload[0..min(self.payload.len(), self.length)],
62            MavLinkVersion::V2 => {
63                &self.payload[0..min(self.payload.len(), Self::truncated_length(&self.payload))]
64            }
65        };
66
67        f.debug_struct("payload")
68            .field("id", &self.id)
69            .field("payload", &payload)
70            .field("length", &self.length)
71            .field("version", &self.version)
72            .finish()
73    }
74}
75
76impl Payload {
77    pub fn new(id: MessageId, payload: &[u8], version: MavLinkVersion) -> Self {
88        let max_size = min(PAYLOAD_MAX_SIZE, payload.len());
89
90        let length = match version {
92            MavLinkVersion::V1 => max_size,
93            MavLinkVersion::V2 => Self::truncated_length(&payload[0..max_size]),
94        };
95
96        let payload = Self::container_from_slice(payload, length);
97
98        Self {
99            id,
100            payload,
101            length,
102            version,
103        }
104    }
105
106    pub fn id(&self) -> MessageId {
108        self.id
109    }
110
111    pub fn bytes(&self) -> &[u8] {
116        &self.payload[0..self.length]
117    }
118
119    pub fn bytes_mut(&mut self) -> &mut [u8] {
124        #[cfg(feature = "alloc")]
125        let slice = &mut self.payload.as_mut_slice()[0..self.length];
126        #[cfg(not(feature = "alloc"))]
127        let slice = &mut self.payload.content.as_mut_slice()[0..self.length];
128        slice
129    }
130
131    pub fn version(&self) -> MavLinkVersion {
135        self.version
136    }
137
138    pub fn length(&self) -> u8 {
144        self.length as u8
145    }
146
147    pub fn upgrade(&mut self) {
154        self.version = MavLinkVersion::V2;
155        self.length = Self::truncated_length(self.bytes());
156    }
157
158    pub fn upgraded(self) -> Self {
165        Self::new(self.id, self.bytes(), MavLinkVersion::V2)
166    }
167
168    fn truncated_length(slice: &[u8]) -> usize {
169        let n: usize = slice.len();
170        let mut num_non_zero = 0usize;
172        for i in 1..=n {
174            if slice[n - i] != 0u8 {
176                num_non_zero = n - i + 1;
177                break;
178            }
179        }
180
181        core::cmp::max(num_non_zero, 1)
182    }
183
184    fn container_from_slice(value: &[u8], max_size: usize) -> PayloadContainer {
185        let value: &[u8] = if value.len() > max_size {
187            &value[0..max_size]
188        } else {
189            value
190        };
191
192        #[cfg(not(feature = "alloc"))]
193        let payload: PayloadContainer = PayloadContainer {
194            content: {
195                let mut no_std_payload = [0u8; PAYLOAD_MAX_SIZE];
196                let max_len = if PAYLOAD_MAX_SIZE < value.len() {
197                    PAYLOAD_MAX_SIZE
198                } else {
199                    value.len()
200                };
201                no_std_payload[0..max_len].copy_from_slice(&value[0..max_len]);
202                no_std_payload
203            },
204        };
205        #[cfg(feature = "alloc")]
206        let payload: PayloadContainer = if value.len() < max_size {
207            let mut payload = alloc::vec![0u8; max_size];
208            payload[0..value.len()].copy_from_slice(value);
209            payload
210        } else {
211            PayloadContainer::from(value)
212        };
213
214        payload
215    }
216
217    fn container_default() -> PayloadContainer {
218        PayloadContainer::default()
219    }
220}
221
222pub trait IntoPayload {
226    fn encode(&self, version: MavLinkVersion) -> Result<Payload, SpecError>;
233}
234
235#[cfg(not(feature = "alloc"))]
236mod no_alloc_payload_container {
237    use crate::payload::PAYLOAD_MAX_SIZE;
238
239    #[derive(Clone, Debug)]
240    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241    pub struct PayloadContainer {
242        #[cfg_attr(feature = "serde", serde(with = "serde_arrays"))]
243        pub(super) content: [u8; PAYLOAD_MAX_SIZE],
244    }
245
246    impl Default for PayloadContainer {
247        fn default() -> Self {
248            Self {
249                content: [0u8; PAYLOAD_MAX_SIZE],
250            }
251        }
252    }
253
254    impl<Idx> core::ops::Index<Idx> for PayloadContainer
255    where
256        Idx: core::slice::SliceIndex<[u8]>,
257    {
258        type Output = Idx::Output;
259
260        fn index(&self, index: Idx) -> &Self::Output {
261            &self.content[index]
262        }
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn new_payload() {
272        let payload = Payload::new(0, &[1, 2, 3, 4, 5, 6u8], MavLinkVersion::V1);
274        assert_eq!(payload.length(), 6);
275        assert_eq!(payload.bytes().len(), 6);
276        assert_eq!(payload.bytes(), &[1, 2, 3, 4, 5, 6u8]);
277
278        let payload = Payload::new(0, &[1, 2, 3, 4, 0, 0u8], MavLinkVersion::V1);
280        assert_eq!(payload.length(), 6);
281        assert_eq!(payload.bytes().len(), 6);
282
283        let payload = Payload::new(0, &[1, 2, 3, 4, 0, 0u8], MavLinkVersion::V2);
285        assert_eq!(payload.length(), 4);
286        assert_eq!(payload.bytes().len(), 4);
287
288        let payload = Payload::new(0, &[0, 0, 0, 0, 0, 0u8], MavLinkVersion::V2);
290        assert_eq!(payload.length(), 1);
291        assert_eq!(payload.bytes().len(), 1);
292
293        let payload = Payload::new(0, &[1u8; PAYLOAD_MAX_SIZE * 2], MavLinkVersion::V1);
295        assert_eq!(payload.length() as usize, PAYLOAD_MAX_SIZE);
296        assert_eq!(payload.bytes().len(), PAYLOAD_MAX_SIZE);
297    }
298
299    #[test]
300    fn truncated_length() {
301        assert_eq!(Payload::truncated_length(&[1, 2, 3, 4, 5, 6u8]), 6);
302        assert_eq!(Payload::truncated_length(&[1, 2, 3, 4, 0, 0u8]), 4);
303        assert_eq!(Payload::truncated_length(&[0, 0, 0, 0, 0, 0u8]), 1);
304    }
305}