use crate::profile::{FieldInfo, MesgInfo, SubField};
use crate::transforms::components::scalar_as_u64;
use crate::transforms::enum_strings::enum_str_by_value;
use crate::RawMessage;
pub fn select<'a>(
parent_field: &'a FieldInfo,
parent_mesg: &MesgInfo,
raw_message: &RawMessage<'_>,
) -> Option<&'a SubField> {
if parent_field.sub_fields.is_empty() {
return None;
}
for sub in parent_field.sub_fields {
for (ref_name, expected) in sub.conditions {
if condition_matches(parent_mesg, raw_message, ref_name, expected) {
return Some(sub);
}
}
}
None
}
fn condition_matches(
parent_mesg: &MesgInfo,
raw_message: &RawMessage<'_>,
ref_field_name: &str,
expected: &str,
) -> bool {
let Some(ref_field) = parent_mesg.field_by_name(ref_field_name) else {
return false;
};
let Some(actual) = raw_message.field(ref_field.field_def_num) else {
return false;
};
let Some(actual_u64) = scalar_as_u64(&actual.value) else {
return false;
};
if let Some(expected_int) = parse_int_literal(expected) {
return actual_u64 == expected_int;
}
if let Some(actual_str) = enum_str_by_value(ref_field.type_name, actual_u64) {
return actual_str == expected;
}
false
}
fn parse_int_literal(s: &str) -> Option<u64> {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
return u64::from_str_radix(hex, 16).ok();
}
s.parse::<u64>().ok()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::profile::generated::messages;
use crate::raw_value::RawValue;
#[test]
fn parse_int_literal_accepts_hex_and_decimal() {
assert_eq!(parse_int_literal("16"), Some(16));
assert_eq!(parse_int_literal("0x10"), Some(16));
assert_eq!(parse_int_literal("0X10"), Some(16));
assert_eq!(parse_int_literal("running"), None);
}
#[test]
fn file_id_product_subfield_resolves_for_garmin_manufacturer() {
let file_id = messages::ALL_MESSAGES
.iter()
.find(|m| m.name == "file_id")
.expect("file_id must exist");
let product_field = file_id
.field_by_name("product")
.expect("file_id.product must exist");
assert!(!product_field.sub_fields.is_empty());
let raw = RawMessage {
global_mesg_num: 0,
fields: vec![
crate::RawField {
field_def_num: 1, value: RawValue::U16Scalar(1),
},
crate::RawField {
field_def_num: 2, value: RawValue::U16Scalar(0),
},
],
dev_fields: vec![],
starts_new_chain: false,
};
let sub = select(product_field, file_id, &raw)
.expect("garmin_product subfield must match for manufacturer=1");
assert_eq!(sub.name, "garmin_product");
}
#[test]
fn returns_none_when_no_condition_matches() {
let file_id = messages::ALL_MESSAGES
.iter()
.find(|m| m.name == "file_id")
.unwrap();
let product = file_id.field_by_name("product").unwrap();
let raw = RawMessage {
global_mesg_num: 0,
fields: vec![crate::RawField {
field_def_num: 1,
value: RawValue::U16Scalar(999),
}],
dev_fields: vec![],
starts_new_chain: false,
};
assert!(select(product, file_id, &raw).is_none());
}
}