Skip to content

Commit a41d77b

Browse files
committed
messages: Parse enum types
With this patch, values for enum fields are automatically parsed into the corresponding enum types when using the message structs. If a value cannot be parsed correctly, it is added to a invalid_fields field so that users can manually parse that data if they want to.
1 parent 5177185 commit a41d77b

File tree

7 files changed

+20049
-5937
lines changed

7 files changed

+20049
-5937
lines changed

fitparser/src/lib.rs

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ impl FitDataField {
136136
pub fn into_value(self) -> Value {
137137
self.value
138138
}
139+
140+
/// Consume the field and return the value with associated units
141+
pub fn into_value_with_units(self) -> ValueWithUnits {
142+
ValueWithUnits::new(self.value, self.units)
143+
}
139144
}
140145

141146
impl fmt::Display for FitDataField {
@@ -326,7 +331,7 @@ impl convert::TryInto<i64> for &Value {
326331
/// Describes a field value along with its defined units (if any), this struct is useful for
327332
/// serializing data in a key-value store where the key is either the name or definition number
328333
/// since it can be created from a `FitDataField` with minimal data cloning.
329-
#[derive(Clone, Debug, Serialize)]
334+
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize)]
330335
pub struct ValueWithUnits {
331336
value: Value,
332337
units: String,
@@ -337,6 +342,16 @@ impl ValueWithUnits {
337342
pub fn new(value: Value, units: String) -> Self {
338343
ValueWithUnits { value, units }
339344
}
345+
346+
/// Return a reference to the stored value
347+
pub fn value(&self) -> &Value {
348+
&self.value
349+
}
350+
351+
/// Return units associated with the value
352+
pub fn units(&self) -> &str {
353+
&self.units
354+
}
340355
}
341356

342357
impl convert::From<FitDataField> for ValueWithUnits {
@@ -361,104 +376,104 @@ mod tests {
361376
use super::*;
362377
use std::collections::HashSet;
363378

379+
fn validate_records(records: Vec<FitDataRecord>, ignore_unexpected_fields: bool) {
380+
let mut options = MessageParseOptions::default();
381+
options.ignore_unexpected_fields = ignore_unexpected_fields;
382+
for record in records {
383+
validate_record(record, options);
384+
}
385+
}
386+
387+
fn validate_record(record: FitDataRecord, options: MessageParseOptions) {
388+
if !MesgNum::is_named_variant(record.kind().as_i64()) {
389+
return;
390+
}
391+
let message = Message::parse_with_options(record, options).unwrap();
392+
assert!(
393+
message.invalid_fields().is_empty(),
394+
"{:?}",
395+
message.invalid_fields()
396+
);
397+
}
398+
364399
#[test]
365400
fn parse_activity() {
366401
let data = include_bytes!("../tests/fixtures/Activity.fit").to_vec();
367402
let fit_data = from_bytes(&data).unwrap();
368403
assert_eq!(fit_data.len(), 22);
369-
for record in fit_data {
370-
assert!(Message::parse(record).is_ok());
371-
}
404+
validate_records(fit_data, false);
372405
}
373406

374407
#[test]
375408
fn parse_developer_data() {
376409
let data = include_bytes!("../tests/fixtures/DeveloperData.fit").to_vec();
377410
let fit_data = from_bytes(&data).unwrap();
378411
assert_eq!(fit_data.len(), 6);
379-
for record in fit_data {
380-
assert!(Message::parse(record).is_ok());
381-
}
412+
validate_records(fit_data, false);
382413
}
383414

384415
#[test]
385416
fn parse_monitoring_file() {
386417
let data = include_bytes!("../tests/fixtures/MonitoringFile.fit").to_vec();
387418
let fit_data = from_bytes(&data).unwrap();
388419
assert_eq!(fit_data.len(), 355);
389-
for record in fit_data {
390-
assert!(Message::parse(record).is_ok());
391-
}
420+
validate_records(fit_data, false);
392421
}
393422

394423
#[test]
395424
fn parse_settings() {
396425
let data = include_bytes!("../tests/fixtures/Settings.fit").to_vec();
397426
let fit_data = from_bytes(&data).unwrap();
398427
assert_eq!(fit_data.len(), 3);
399-
for record in fit_data {
400-
assert!(Message::parse(record).is_ok());
401-
}
428+
validate_records(fit_data, false);
402429
}
403430

404431
#[test]
405432
fn parse_weight_scale_multi_user() {
406433
let data = include_bytes!("../tests/fixtures/WeightScaleMultiUser.fit").to_vec();
407434
let fit_data = from_bytes(&data).unwrap();
408435
assert_eq!(fit_data.len(), 7);
409-
for record in fit_data {
410-
assert!(Message::parse(record).is_ok());
411-
}
436+
validate_records(fit_data, false);
412437
}
413438

414439
#[test]
415440
fn parse_weight_scale_single_user() {
416441
let data = include_bytes!("../tests/fixtures/WeightScaleSingleUser.fit").to_vec();
417442
let fit_data = from_bytes(&data).unwrap();
418443
assert_eq!(fit_data.len(), 6);
419-
for record in fit_data {
420-
assert!(Message::parse(record).is_ok());
421-
}
444+
validate_records(fit_data, false);
422445
}
423446

424447
#[test]
425448
fn parse_workout_custom_target_values() {
426449
let data = include_bytes!("../tests/fixtures/WorkoutCustomTargetValues.fit").to_vec();
427450
let fit_data = from_bytes(&data).unwrap();
428451
assert_eq!(fit_data.len(), 6);
429-
for record in fit_data {
430-
assert!(Message::parse(record).is_ok());
431-
}
452+
validate_records(fit_data, false);
432453
}
433454

434455
#[test]
435456
fn parse_workout_individual_steps() {
436457
let data = include_bytes!("../tests/fixtures/WorkoutIndividualSteps.fit").to_vec();
437458
let fit_data = from_bytes(&data).unwrap();
438459
assert_eq!(fit_data.len(), 6);
439-
for record in fit_data {
440-
assert!(Message::parse(record).is_ok());
441-
}
460+
validate_records(fit_data, false);
442461
}
443462

444463
#[test]
445464
fn parse_workout_repeat_greater_than_step() {
446465
let data = include_bytes!("../tests/fixtures/WorkoutRepeatGreaterThanStep.fit").to_vec();
447466
let fit_data = from_bytes(&data).unwrap();
448467
assert_eq!(fit_data.len(), 7);
449-
for record in fit_data {
450-
assert!(Message::parse(record).is_ok());
451-
}
468+
validate_records(fit_data, false);
452469
}
453470

454471
#[test]
455472
fn parse_workout_repeat_steps() {
456473
let data = include_bytes!("../tests/fixtures/WorkoutRepeatSteps.fit").to_vec();
457474
let fit_data = from_bytes(&data).unwrap();
458475
assert_eq!(fit_data.len(), 7);
459-
for record in fit_data {
460-
assert!(Message::parse(record).is_ok());
461-
}
476+
validate_records(fit_data, false);
462477
}
463478

464479
#[test]
@@ -468,13 +483,7 @@ mod tests {
468483
let data = include_bytes!("../tests/fixtures/garmin-fenix-5-bike.fit").to_vec();
469484
let fit_data = from_bytes(&data).unwrap();
470485
assert_eq!(fit_data.len(), 143);
471-
let mut options = MessageParseOptions::default();
472-
options.ignore_unexpected_fields = true;
473-
for record in fit_data {
474-
if MesgNum::is_named_variant(record.kind().as_i64()) {
475-
assert!(Message::parse_with_options(record, options).is_ok());
476-
}
477-
}
486+
validate_records(fit_data, true);
478487
}
479488

480489
#[test]
@@ -483,13 +492,7 @@ mod tests {
483492
let data = include_bytes!("../tests/fixtures/sample_mulitple_header.fit").to_vec();
484493
let fit_data = from_bytes(&data).unwrap();
485494
assert_eq!(fit_data.len(), 3023);
486-
let mut options = MessageParseOptions::default();
487-
options.ignore_unexpected_fields = true;
488-
for record in fit_data {
489-
if MesgNum::is_named_variant(record.kind().as_i64()) {
490-
assert!(Message::parse_with_options(record, options).is_ok());
491-
}
492-
}
495+
validate_records(fit_data, true);
493496
}
494497

495498
#[test]

0 commit comments

Comments
 (0)