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#[derive(Debug)]
14#[allow(missing_docs)]
15pub enum MessageValidationError {
16 EmptyMessageData,
18 UnsupportedDeveloperData,
20 UnsupportedFitBaseType {
22 field_index: usize,
23 base_type: FitBaseType,
24 },
25 FieldValidation {
27 field_index: usize,
28 err: FieldValidationError,
29 },
30 DeveloperFieldValidation {
32 developer_field_index: usize,
33 err: FieldValidationError,
34 },
35 MissingDeveloperDataId { developer_field_index: usize },
37 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#[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 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}