1use std::borrow::Cow;
24
25use crate::base_type::BaseType;
26#[cfg(feature = "chrono")]
27use crate::datetime;
28use crate::decoder::Decoder;
29use crate::dev_fields::{self, DevFieldRegistry};
30use crate::error::FitError;
31use crate::profile;
32use crate::raw_value::RawValue;
33use crate::stream::Endian;
34use crate::transforms::{components, enum_strings, scale_offset, subfields, Accumulator};
35use crate::value::{Field, FieldKind, Message, Value};
36use crate::{RawDevField, RawField, RawMessage};
37
38type MesgCallback<'a> = Box<dyn Fn(&Message) + 'a>;
40
41#[derive(Debug, Clone, Copy)]
45pub struct TransformOptions {
46 pub apply_scale_and_offset: bool,
47 pub expand_components: bool,
48 pub expand_subfields: bool,
49 pub convert_types_to_strings: bool,
50 pub convert_datetime: bool,
51 pub decode_memo_glob: bool,
53 #[cfg(feature = "chrono")]
58 pub merge_heart_rates: bool,
59 pub skip_header: bool,
61 pub data_only: bool,
64}
65
66impl Default for TransformOptions {
67 fn default() -> Self {
68 Self {
69 apply_scale_and_offset: true,
70 expand_components: true,
71 expand_subfields: true,
72 convert_types_to_strings: true,
73 convert_datetime: true,
74 decode_memo_glob: false,
75 #[cfg(feature = "chrono")]
76 merge_heart_rates: false,
77 skip_header: false,
78 data_only: false,
79 }
80 }
81}
82
83pub struct DecoderBuilder<'a> {
85 bytes: &'a [u8],
86 options: TransformOptions,
87 on_mesg: Option<MesgCallback<'a>>,
88}
89
90impl<'a> DecoderBuilder<'a> {
91 pub fn new(bytes: &'a [u8]) -> Self {
92 Self {
93 bytes,
94 options: TransformOptions::default(),
95 on_mesg: None,
96 }
97 }
98
99 pub fn apply_scale_and_offset(mut self, v: bool) -> Self {
100 self.options.apply_scale_and_offset = v;
101 self
102 }
103 pub fn expand_components(mut self, v: bool) -> Self {
104 self.options.expand_components = v;
105 self
106 }
107 pub fn expand_subfields(mut self, v: bool) -> Self {
108 self.options.expand_subfields = v;
109 self
110 }
111 pub fn convert_types_to_strings(mut self, v: bool) -> Self {
112 self.options.convert_types_to_strings = v;
113 self
114 }
115 pub fn convert_datetime(mut self, v: bool) -> Self {
116 self.options.convert_datetime = v;
117 self
118 }
119 pub fn decode_memo_glob(mut self, v: bool) -> Self {
120 self.options.decode_memo_glob = v;
121 self
122 }
123 #[cfg(feature = "chrono")]
124 pub fn merge_heart_rates(mut self, v: bool) -> Self {
125 self.options.merge_heart_rates = v;
126 self
127 }
128 pub fn skip_header(mut self, v: bool) -> Self {
129 self.options.skip_header = v;
130 self
131 }
132 pub fn data_only(mut self, v: bool) -> Self {
133 self.options.data_only = v;
134 self
135 }
136
137 pub fn on_mesg<F: Fn(&Message) + 'a>(mut self, f: F) -> Self {
139 self.on_mesg = Some(Box::new(f));
140 self
141 }
142
143 pub fn build(self) -> TypedDecoder<'a> {
144 TypedDecoder {
145 inner: Decoder::new(self.bytes),
146 options: self.options,
147 accumulator: Accumulator::new(),
148 dev_registry: DevFieldRegistry::new(),
149 on_mesg: self.on_mesg,
150 }
151 }
152}
153
154impl<'a> Decoder<'a> {
155 pub fn builder(bytes: &'a [u8]) -> DecoderBuilder<'a> {
159 DecoderBuilder::new(bytes)
160 }
161}
162
163pub struct TypedDecoder<'a> {
166 inner: Decoder<'a>,
167 options: TransformOptions,
168 pub(crate) accumulator: Accumulator,
169 dev_registry: DevFieldRegistry,
170 on_mesg: Option<MesgCallback<'a>>,
171}
172
173impl<'a> TypedDecoder<'a> {
174 pub fn read_all(mut self) -> (Vec<Message>, Vec<FitError>) {
176 let mut messages = Vec::new();
177 let mut errors = Vec::new();
178 for item in self.by_ref() {
179 match item {
180 Ok(m) => messages.push(m),
181 Err(e) => errors.push(e),
182 }
183 }
184
185 if self.options.skip_header {
187 messages.retain(|m| m.global_mesg_num != 0); }
189
190 if self.options.data_only {
192 messages.retain(|m| crate::profile::mesg_info_by_num(m.global_mesg_num).is_some());
193 }
194
195 if self.options.decode_memo_glob {
197 crate::transforms::memo_glob::decode_memo_glob(&mut messages);
198 }
199
200 #[cfg(feature = "chrono")]
202 if self.options.merge_heart_rates {
203 crate::transforms::merge_heart_rates(&mut messages);
204 }
205
206 (messages, errors)
207 }
208}
209
210impl<'a> Iterator for TypedDecoder<'a> {
211 type Item = Result<Message, FitError>;
212
213 fn next(&mut self) -> Option<Self::Item> {
214 match self.inner.next()? {
215 Ok(raw) => {
216 if raw.starts_new_chain {
220 self.accumulator.clear();
221 }
222 collect_dev_meta(&raw, &mut self.dev_registry);
223 let msg = transform_message(
224 &raw,
225 self.options,
226 &mut self.accumulator,
227 &self.dev_registry,
228 );
229 if let Some(ref cb) = self.on_mesg {
230 cb(&msg);
231 }
232 Some(Ok(msg))
233 }
234 Err(e) => Some(Err(e)),
235 }
236 }
237}
238
239fn collect_dev_meta(raw: &RawMessage<'_>, registry: &mut DevFieldRegistry) {
246 match raw.global_mesg_num {
247 207 => {}
250 206 => {
252 let dev_idx = raw.field(0).and_then(|f| f.value.as_u8()).unwrap_or(0);
253 let fdn = raw.field(1).and_then(|f| f.value.as_u8()).unwrap_or(0);
254 let fit_base_type_id = raw.field(2).and_then(|f| f.value.as_u8()).unwrap_or(0xFF);
255 let field_name = raw
256 .field(3)
257 .and_then(|f| f.value.as_str())
258 .unwrap_or("unknown")
259 .to_string();
260 let scale_raw = raw.field(6).and_then(|f| f.value.as_u8());
261 let offset_raw = raw.field(7).and_then(|f| f.value.as_u8());
262 let units_str = raw
263 .field(8)
264 .and_then(|f| f.value.as_str())
265 .map(|s| s.to_string());
266
267 let scale = scale_raw.map(|v| v as f64).filter(|&s| s != 1.0);
268 let offset = offset_raw.map(|v| v as f64).filter(|&o| o != 0.0);
269
270 registry.register_field(
271 dev_idx,
272 fdn,
273 field_name,
274 fit_base_type_id,
275 scale,
276 offset,
277 units_str,
278 );
279 }
280 _ => {}
281 }
282}
283
284fn transform_message(
285 raw: &RawMessage<'_>,
286 options: TransformOptions,
287 acc: &mut Accumulator,
288 dev_registry: &DevFieldRegistry,
289) -> Message {
290 let mesg_info = profile::mesg_info_by_num(raw.global_mesg_num);
291 let name = mesg_info.map(|m| m.name).unwrap_or("unknown");
292
293 let mut fields = Vec::with_capacity(raw.fields.len() + 4);
294
295 for rf in &raw.fields {
296 let Some(mesg) = mesg_info else {
297 fields.push(unknown_field(rf));
298 continue;
299 };
300 let Some(fi) = mesg.field(rf.field_def_num) else {
301 fields.push(unknown_field(rf));
302 continue;
303 };
304
305 let selected_subfield: Option<&crate::profile::SubField> = if options.expand_subfields {
307 subfields::select(fi, mesg, raw)
308 } else {
309 None
310 };
311 let (effective_name, type_name, scale, offset, units) = match selected_subfield {
312 Some(sub) => (sub.name, sub.type_name, fi.scale, fi.offset, fi.units),
313 None => (fi.name, fi.type_name, fi.scale, fi.offset, fi.units),
314 };
315
316 let raw_for_transform = if fi.accumulate {
318 if let Some(scalar) = components::scalar_as_u64(&rf.value) {
319 let bits = resolve_accumulator_bits(fi.type_name);
320 let accumulated =
321 acc.accumulate(raw.global_mesg_num, rf.field_def_num, scalar, bits);
322 RawValue::U64Scalar(accumulated)
323 } else {
324 rf.value.clone()
325 }
326 } else {
327 rf.value.clone()
328 };
329
330 let value = transform_value(&raw_for_transform, type_name, scale, offset, options);
332 fields.push(Field {
333 name: effective_name.to_string(),
334 kind: FieldKind::Standard {
335 field_def_num: rf.field_def_num,
336 },
337 value,
338 units: units.map(str::to_string),
339 });
340
341 if options.expand_components {
343 if !fi.components.is_empty() {
345 if let Some(scalar) = components::scalar_as_u64(&rf.value) {
346 for comp in components::unpack_scalar(fi.components, scalar) {
347 let comp_raw = if comp.accumulate {
348 acc.accumulate(
349 raw.global_mesg_num,
350 rf.field_def_num,
351 comp.raw,
352 comp.bits as u32,
353 )
354 } else {
355 comp.raw
356 };
357 let raw_v = RawValue::U64Scalar(comp_raw);
358 let cv =
359 transform_value(&raw_v, "uint64", comp.scale, comp.offset, options);
360 fields.push(Field {
361 name: comp.target_name.to_string(),
362 kind: FieldKind::Standard { field_def_num: 0 },
363 value: cv,
364 units: comp.units.map(str::to_string),
365 });
366 }
367 }
368 }
369 if let Some(sub) = selected_subfield {
371 if !sub.components.is_empty() {
372 if let Some(scalar) = components::scalar_as_u64(&rf.value) {
373 for comp in components::unpack_scalar(sub.components, scalar) {
374 let comp_raw = if comp.accumulate {
375 acc.accumulate(
376 raw.global_mesg_num,
377 rf.field_def_num,
378 comp.raw,
379 comp.bits as u32,
380 )
381 } else {
382 comp.raw
383 };
384 let raw_v = RawValue::U64Scalar(comp_raw);
385 let cv =
386 transform_value(&raw_v, "uint64", comp.scale, comp.offset, options);
387 fields.push(Field {
388 name: comp.target_name.to_string(),
389 kind: FieldKind::Standard { field_def_num: 0 },
390 value: cv,
391 units: comp.units.map(str::to_string),
392 });
393 }
394 }
395 }
396 }
397 }
398 }
399
400 for dev in &raw.dev_fields {
402 if let Some(info) = dev_registry.get(dev.developer_data_index, dev.field_def_num) {
403 let value = resolve_dev_field(dev, info, options);
404 fields.push(Field {
405 name: info.name.clone(),
406 kind: FieldKind::Developer {
407 field_def_num: dev.field_def_num,
408 developer_data_index: dev.developer_data_index,
409 },
410 value,
411 units: info.units.clone(),
412 });
413 } else {
414 fields.push(Field {
415 name: "developer_field".to_string(),
416 kind: FieldKind::Developer {
417 field_def_num: dev.field_def_num,
418 developer_data_index: dev.developer_data_index,
419 },
420 value: Value::Bytes(dev.bytes.clone().into_owned()),
421 units: None,
422 });
423 }
424 }
425
426 Message {
427 global_mesg_num: raw.global_mesg_num,
428 name,
429 fields,
430 }
431}
432
433fn unknown_field(rf: &RawField) -> Field {
434 Field {
435 name: "unknown".to_string(),
436 kind: FieldKind::Standard {
437 field_def_num: rf.field_def_num,
438 },
439 value: raw_to_value_passthrough(&rf.value),
440 units: None,
441 }
442}
443
444fn resolve_accumulator_bits(type_name: &str) -> u32 {
450 if let Some(bt) = crate::transforms::enum_strings::base_type_for_type_name(type_name) {
452 return (bt.element_size() * 8) as u32;
453 }
454 let bt = match type_name {
456 "enum" | "sint8" | "uint8" | "uint8z" | "byte" | "string" | "bool" => BaseType::UInt8,
457 "sint16" | "uint16" | "uint16z" => BaseType::UInt16,
458 "sint32" | "uint32" | "uint32z" | "float32" | "date_time" | "local_date_time" => {
459 BaseType::UInt32
460 }
461 "sint64" | "uint64" | "uint64z" | "float64" => BaseType::UInt64,
462 _ => BaseType::UInt32,
463 };
464 (bt.element_size() * 8) as u32
465}
466
467fn resolve_dev_field(
470 dev: &RawDevField<'_>,
471 info: &dev_fields::DevFieldInfo,
472 options: TransformOptions,
473) -> Value {
474 let raw = match crate::raw_value::decode_value(
477 info.base_type,
478 &dev.bytes,
479 Endian::Little,
480 dev.field_def_num,
481 ) {
482 Ok(v) => v,
483 Err(_) => return Value::Bytes(dev.bytes.clone().into_owned()),
484 };
485
486 let type_name = dev_fields::base_type_to_type_name(info.base_type);
487 transform_value(&raw, type_name, info.scale, info.offset, options)
488}
489
490fn transform_value(
491 raw: &RawValue,
492 type_name: &str,
493 scale: Option<f64>,
494 offset: Option<f64>,
495 options: TransformOptions,
496) -> Value {
497 if matches!(raw, RawValue::Invalid) {
498 return Value::Invalid;
499 }
500
501 if options.convert_datetime && (type_name == "date_time" || type_name == "local_date_time") {
505 if let Some(secs) = components::scalar_as_u64(raw) {
506 if secs <= u32::MAX as u64 {
507 #[cfg(feature = "chrono")]
508 {
509 if let Some(dt) = datetime::fit_to_datetime(secs as u32) {
510 return Value::DateTime(dt);
511 }
512 }
513 #[cfg(not(feature = "chrono"))]
514 {
515 return Value::DateTime(secs as u32);
516 }
517 }
518 }
519 }
520
521 if options.convert_types_to_strings {
523 if let Some(v) = components::scalar_as_u64(raw) {
524 if let Some(s) = enum_strings::enum_str_by_value(type_name, v) {
525 return Value::Enum(Cow::Borrowed(s));
528 }
529 }
530 }
531
532 if options.apply_scale_and_offset && !scale_offset::is_identity(scale, offset) {
534 if let Some(v) = scalar_as_f64(raw) {
535 return Value::Float(scale_offset::apply(v, scale, offset));
536 }
537 if let Some(arr) = array_as_f64s(raw) {
538 return Value::Array(
539 arr.into_iter()
540 .map(|v| Value::Float(scale_offset::apply(v, scale, offset)))
541 .collect(),
542 );
543 }
544 }
545
546 raw_to_value_passthrough(raw)
547}
548
549fn raw_to_value_passthrough(raw: &RawValue) -> Value {
550 use RawValue::*;
551 match raw {
552 Invalid => Value::Invalid,
553 String(s) => Value::String(s.to_string()),
554 Byte(v) => Value::Bytes(v.to_vec()),
555
556 EnumScalar(v) | U8Scalar(v) | U8zScalar(v) => Value::UInt(*v as u64),
558 U16Scalar(v) | U16zScalar(v) => Value::UInt(*v as u64),
559 U32Scalar(v) | U32zScalar(v) => Value::UInt(*v as u64),
560 U64Scalar(v) | U64zScalar(v) => Value::UInt(*v),
561 I8Scalar(v) => Value::SInt(*v as i64),
562 I16Scalar(v) => Value::SInt(*v as i64),
563 I32Scalar(v) => Value::SInt(*v as i64),
564 I64Scalar(v) => Value::SInt(*v),
565 F32Scalar(v) => Value::Float(*v as f64),
566 F64Scalar(v) => Value::Float(*v),
567
568 EnumArray(a) | U8Array(a) | U8zArray(a) => {
570 Value::Array(a.iter().map(|x| Value::UInt(*x as u64)).collect())
571 }
572 U16Array(a) | U16zArray(a) => {
573 Value::Array(a.iter().map(|x| Value::UInt(*x as u64)).collect())
574 }
575 U32Array(a) | U32zArray(a) => {
576 Value::Array(a.iter().map(|x| Value::UInt(*x as u64)).collect())
577 }
578 U64Array(a) | U64zArray(a) => Value::Array(a.iter().map(|x| Value::UInt(*x)).collect()),
579 I8Array(a) => Value::Array(a.iter().map(|x| Value::SInt(*x as i64)).collect()),
580 I16Array(a) => Value::Array(a.iter().map(|x| Value::SInt(*x as i64)).collect()),
581 I32Array(a) => Value::Array(a.iter().map(|x| Value::SInt(*x as i64)).collect()),
582 I64Array(a) => Value::Array(a.iter().map(|x| Value::SInt(*x)).collect()),
583 F32Array(a) => Value::Array(a.iter().map(|x| Value::Float(*x as f64)).collect()),
584 F64Array(a) => Value::Array(a.iter().map(|x| Value::Float(*x)).collect()),
585 }
586}
587
588#[inline]
589fn scalar_as_f64(raw: &RawValue) -> Option<f64> {
590 raw.scalar_f64()
591}
592
593#[inline]
594fn array_as_f64s(raw: &RawValue) -> Option<Vec<f64>> {
595 raw.to_f64s()
599}