Skip to main content

rustyfit/encoder/
validator.rs

1#![warn(missing_docs)]
2
3use crate::{
4    profile::{
5        mesgdef,
6        typedef::{FitBaseType, MesgNum},
7    },
8    proto::{LocalFieldDescription, Message, ProtocolVersion, Value},
9};
10use alloc::vec::Vec;
11
12/// Error occurs during message validation
13#[derive(Debug)]
14#[allow(missing_docs)]
15pub enum MessageValidationError {
16    /// Message has zero fields and zero developer fields.
17    EmptyMessageData,
18    /// Developer data is unsupported in protocol v1.
19    UnsupportedDeveloperData,
20    /// Some base types are unsupported in protocol v1.
21    UnsupportedFitBaseType {
22        field_index: usize,
23        base_type: FitBaseType,
24    },
25    /// Field contains invalid data.
26    FieldValidation {
27        field_index: usize,
28        err: FieldValidationError,
29    },
30    /// DeveloperField contains invalid data.
31    DeveloperFieldValidation {
32        developer_field_index: usize,
33        err: FieldValidationError,
34    },
35    /// Developer fields must have prior DeveloperDataId message.
36    MissingDeveloperDataId { developer_field_index: usize },
37    /// Developer fields must have prior FieldDescription message.
38    MissingFieldDescription { developer_field_index: usize },
39}
40
41impl core::fmt::Display for MessageValidationError {
42    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43        match &self {
44            Self::EmptyMessageData => {
45                write!(f, "message has zero fields and zero developer fields")
46            }
47            Self::UnsupportedDeveloperData => {
48                write!(f, "developer data is unsupported for protocol v1")
49            }
50            Self::UnsupportedFitBaseType {
51                field_index,
52                base_type,
53            } => write!(
54                f,
55                "fit base type {} on field index {} is unsupported for protocol v1",
56                base_type, field_index
57            ),
58            Self::FieldValidation { field_index, err } => {
59                write!(f, "field validation: field index {}: {}", field_index, err)
60            }
61            Self::DeveloperFieldValidation {
62                developer_field_index,
63                err,
64            } => write!(
65                f,
66                "developer field validation: developer field index {}: {}",
67                developer_field_index, err
68            ),
69            Self::MissingDeveloperDataId {
70                developer_field_index,
71            } => write!(
72                f,
73                "missing developer data id for developer field index {}",
74                developer_field_index
75            ),
76            Self::MissingFieldDescription {
77                developer_field_index,
78            } => write!(
79                f,
80                "missing field description for developer field index {}",
81                developer_field_index
82            ),
83        }
84    }
85}
86
87impl core::error::Error for MessageValidationError {}
88
89/// Error occurs during value `fields` or `developer_fields` validation.
90/// The context depends on where the validation takes place.
91#[derive(Debug)]
92#[allow(missing_docs)]
93pub enum FieldValidationError {
94    ValueTypeInvalid,
95    StringValueInvalid,
96    ValueSizeLimitExceeded,
97    FieldLimitExceeded,
98}
99
100impl core::fmt::Display for FieldValidationError {
101    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102        match &self {
103            Self::ValueTypeInvalid => write!(f, "value type is invalid"),
104            Self::StringValueInvalid => write!(f, "string value contains invalid UTF-8 characters"),
105            Self::ValueSizeLimitExceeded => {
106                write!(f, "value's size in bytes exceeds 255 bytes limit")
107            }
108            Self::FieldLimitExceeded => {
109                write!(f, "fields or developer_fields exceeds 255 items limit")
110            }
111        }
112    }
113}
114
115impl core::error::Error for FieldValidationError {}
116
117pub(super) struct MessageValidator {
118    developer_data_index_seen: [u64; 4],
119    field_descriptions: Vec<LocalFieldDescription>,
120}
121
122impl MessageValidator {
123    pub(super) const fn new() -> Self {
124        Self {
125            developer_data_index_seen: [0u64; 4],
126            field_descriptions: Vec::new(),
127        }
128    }
129
130    pub(super) fn validate_message(
131        &mut self,
132        mesg: &mut Message,
133        protocol_version: ProtocolVersion,
134    ) -> Result<(), MessageValidationError> {
135        // Some data are unsupported on protocol v1
136        if protocol_version == ProtocolVersion::V1 {
137            if !mesg.developer_fields.is_empty() {
138                return Err(MessageValidationError::UnsupportedDeveloperData);
139            }
140
141            for (i, field) in mesg.fields.iter().enumerate() {
142                if field.profile_type.base_type().0 & FitBaseType::NUM_MASK
143                    > FitBaseType::BYTE.0 & FitBaseType::NUM_MASK
144                {
145                    return Err(MessageValidationError::UnsupportedFitBaseType {
146                        base_type: field.profile_type.base_type(),
147                        field_index: i,
148                    });
149                }
150            }
151        }
152
153        let mut valid = 0usize;
154        for i in 0..mesg.fields.len() {
155            let field = &mesg.fields[i];
156            if field.is_expanded {
157                continue;
158            }
159
160            if let Err(err) = self.validate_field(&field.value, field.profile_type.base_type()) {
161                return Err(MessageValidationError::FieldValidation {
162                    field_index: i,
163                    err,
164                });
165            }
166
167            if valid == 255 {
168                return Err(MessageValidationError::FieldValidation {
169                    field_index: i,
170                    err: FieldValidationError::FieldLimitExceeded,
171                });
172            }
173
174            if i != valid {
175                mesg.fields.swap(i, valid);
176            }
177
178            valid += 1;
179        }
180
181        mesg.fields.truncate(valid);
182
183        match mesg.num {
184            MesgNum::DEVELOPER_DATA_ID => {
185                if let Some(field) = mesg
186                    .fields
187                    .iter()
188                    .find(|field| field.num == mesgdef::DeveloperDataId::DEVELOPER_DATA_INDEX)
189                    && let Value::Uint8(v) = field.value
190                {
191                    self.developer_data_index_seen[(v as usize) >> 6] |= 1 << (v & 63)
192                }
193            }
194            MesgNum::FIELD_DESCRIPTION => self
195                .field_descriptions
196                .push(LocalFieldDescription::from(&*mesg)),
197            _ => {}
198        };
199
200        for i in 0..mesg.developer_fields.len() {
201            let dev_field = &mesg.developer_fields[i];
202
203            let x = dev_field.developer_data_index;
204            if (self.developer_data_index_seen[(x as usize) >> 6] >> (x & 63)) & 1 == 0 {
205                return Err(MessageValidationError::MissingDeveloperDataId {
206                    developer_field_index: i,
207                });
208            }
209
210            match self.field_descriptions.iter().find(|v| {
211                v.developer_data_index == dev_field.developer_data_index
212                    && v.field_definition_number == dev_field.num
213            }) {
214                Some(v) => {
215                    if let Err(err) = self.validate_field(&dev_field.value, v.fit_base_type_id) {
216                        return Err(MessageValidationError::DeveloperFieldValidation {
217                            developer_field_index: i,
218                            err,
219                        });
220                    }
221                }
222                None => {
223                    return Err(MessageValidationError::MissingFieldDescription {
224                        developer_field_index: i,
225                    });
226                }
227            };
228
229            if i == 255 {
230                return Err(MessageValidationError::DeveloperFieldValidation {
231                    developer_field_index: i,
232                    err: FieldValidationError::FieldLimitExceeded,
233                });
234            }
235        }
236
237        if mesg.fields.is_empty() && mesg.developer_fields.is_empty() {
238            return Err(MessageValidationError::EmptyMessageData);
239        }
240
241        Ok(())
242    }
243
244    fn validate_field(
245        &self,
246        value: &Value,
247        base_type: FitBaseType,
248    ) -> Result<(), FieldValidationError> {
249        if !value.is_align(base_type) {
250            return Err(FieldValidationError::ValueTypeInvalid);
251        }
252
253        match value {
254            Value::String(v) if core::str::from_utf8(v.as_bytes()).is_err() => {
255                return Err(FieldValidationError::StringValueInvalid);
256            }
257            Value::VecString(v) => {
258                for x in v.iter() {
259                    if core::str::from_utf8(x.as_bytes()).is_err() {
260                        return Err(FieldValidationError::StringValueInvalid);
261                    }
262                }
263            }
264            _ => {}
265        };
266
267        if value.size() > 255 {
268            return Err(FieldValidationError::ValueSizeLimitExceeded);
269        }
270
271        Ok(())
272    }
273
274    pub(super) fn reset(&mut self) {
275        self.developer_data_index_seen.fill(0);
276        self.field_descriptions.clear();
277        self.field_descriptions.reserve_exact(32);
278    }
279}