use crate::{
MAX_NAME_SIZE, Parser,
attribute::{
AttributeDefinition, AttributeObjectType, AttributeString, AttributeTarget, AttributeValue,
AttributeValueType, EnumValues, MAX_ATTRIBUTE_STRING_SIZE,
},
compat::Name,
};
impl AttributeDefinition {
pub(crate) fn parse(parser: &mut Parser) -> Option<Self> {
parser.skip_newlines_and_spaces();
let object_type = if parser.starts_with(b"BU_") {
parser.expect(b"BU_").ok()?;
AttributeObjectType::Node
} else if parser.starts_with(b"BO_") {
parser.expect(b"BO_").ok()?;
AttributeObjectType::Message
} else if parser.starts_with(b"SG_") {
parser.expect(b"SG_").ok()?;
AttributeObjectType::Signal
} else if parser.starts_with(b"EV_") {
return None;
} else {
AttributeObjectType::Network
};
parser.skip_newlines_and_spaces();
parser.expect(b"\"").ok()?;
let name_bytes = parser.take_until_quote(false, MAX_NAME_SIZE).ok()?;
let name_str = core::str::from_utf8(name_bytes).ok()?;
let name = Name::try_from(name_str).ok()?;
parser.skip_newlines_and_spaces();
let value_type = Self::parse_value_type(parser)?;
Some(AttributeDefinition::new(name, object_type, value_type))
}
fn parse_value_type(parser: &mut Parser) -> Option<AttributeValueType> {
parser.skip_newlines_and_spaces();
if parser.starts_with(b"INT") {
parser.expect(b"INT").ok()?;
let (min, max) = Self::parse_int_range(parser)?;
Some(AttributeValueType::Int { min, max })
} else if parser.starts_with(b"HEX") {
parser.expect(b"HEX").ok()?;
let (min, max) = Self::parse_int_range(parser)?;
Some(AttributeValueType::Hex { min, max })
} else if parser.starts_with(b"FLOAT") {
parser.expect(b"FLOAT").ok()?;
let (min, max) = Self::parse_float_range(parser)?;
Some(AttributeValueType::Float { min, max })
} else if parser.starts_with(b"STRING") {
parser.expect(b"STRING").ok()?;
Some(AttributeValueType::String)
} else if parser.starts_with(b"ENUM") {
parser.expect(b"ENUM").ok()?;
let values = Self::parse_enum_values(parser)?;
Some(AttributeValueType::Enum { values })
} else {
None
}
}
fn parse_int_range(parser: &mut Parser) -> Option<(i64, i64)> {
parser.skip_newlines_and_spaces();
let min = parser.parse_i64().ok()?;
parser.skip_newlines_and_spaces();
let max = parser.parse_i64().ok()?;
Some((min, max))
}
fn parse_float_range(parser: &mut Parser) -> Option<(f64, f64)> {
parser.skip_newlines_and_spaces();
let min = parser.parse_f64().ok()?;
parser.skip_newlines_and_spaces();
let max = parser.parse_f64().ok()?;
Some((min, max))
}
fn parse_enum_values(parser: &mut Parser) -> Option<EnumValues> {
let mut values = EnumValues::new();
loop {
parser.skip_newlines_and_spaces();
if parser.starts_with(b";") || parser.at_newline() || parser.is_empty() {
break;
}
if parser.expect(b"\"").is_err() {
break;
}
let value_bytes = parser.take_until_quote(false, MAX_NAME_SIZE).ok()?;
let value_str = core::str::from_utf8(value_bytes).ok()?;
let value = Name::try_from(value_str).ok()?;
let _ = values.push(value);
parser.skip_newlines_and_spaces();
if parser.starts_with(b",") {
let _ = parser.expect(b",");
}
}
if values.is_empty() {
None
} else {
Some(values)
}
}
}
pub(crate) fn parse_attribute_default(parser: &mut Parser) -> Option<(Name, AttributeValue)> {
parser.skip_newlines_and_spaces();
parser.expect(b"\"").ok()?;
let name_bytes = parser.take_until_quote(false, MAX_NAME_SIZE).ok()?;
let name_str = core::str::from_utf8(name_bytes).ok()?;
let name = Name::try_from(name_str).ok()?;
parser.skip_newlines_and_spaces();
let value = parse_attribute_value(parser)?;
Some((name, value))
}
pub(crate) fn parse_attribute_assignment(
parser: &mut Parser,
) -> Option<(Name, AttributeTarget, AttributeValue)> {
parser.skip_newlines_and_spaces();
parser.expect(b"\"").ok()?;
let name_bytes = parser.take_until_quote(false, MAX_NAME_SIZE).ok()?;
let name_str = core::str::from_utf8(name_bytes).ok()?;
let name = Name::try_from(name_str).ok()?;
parser.skip_newlines_and_spaces();
let target = if parser.starts_with(b"BU_") {
parser.expect(b"BU_").ok()?;
parser.skip_newlines_and_spaces();
let node_name_bytes = parser.parse_identifier().ok()?;
let node_name = Name::try_from(node_name_bytes).ok()?;
AttributeTarget::Node(node_name)
} else if parser.starts_with(b"BO_") {
parser.expect(b"BO_").ok()?;
parser.skip_newlines_and_spaces();
let msg_id = parser.parse_u32().ok()?;
AttributeTarget::Message(msg_id)
} else if parser.starts_with(b"SG_") {
parser.expect(b"SG_").ok()?;
parser.skip_newlines_and_spaces();
let msg_id = parser.parse_u32().ok()?;
parser.skip_newlines_and_spaces();
let signal_name_bytes = parser.parse_identifier().ok()?;
let signal_name = Name::try_from(signal_name_bytes).ok()?;
AttributeTarget::Signal(msg_id, signal_name)
} else if parser.starts_with(b"EV_") {
return None;
} else {
AttributeTarget::Network
};
parser.skip_newlines_and_spaces();
let value = parse_attribute_value(parser)?;
Some((name, target, value))
}
fn parse_attribute_value(parser: &mut Parser) -> Option<AttributeValue> {
parser.skip_newlines_and_spaces();
if parser.starts_with(b"\"") {
parser.expect(b"\"").ok()?;
let str_bytes = parser.take_until_quote(false, MAX_ATTRIBUTE_STRING_SIZE).ok()?;
let str_val = core::str::from_utf8(str_bytes).ok()?;
let attr_str = AttributeString::try_from(str_val).ok()?;
return Some(AttributeValue::String(attr_str));
}
let remaining = parser.remaining();
let has_decimal = remaining
.iter()
.take_while(|&&b| b == b'-' || b == b'+' || b == b'.' || b.is_ascii_digit())
.any(|&b| b == b'.');
if has_decimal {
if let Ok(float_val) = parser.parse_f64() {
return Some(AttributeValue::Float(float_val));
}
} else {
if let Ok(int_val) = parser.parse_i64() {
return Some(AttributeValue::Int(int_val));
}
if let Ok(float_val) = parser.parse_f64() {
return Some(AttributeValue::Float(float_val));
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_ba_def_int() {
let input = b"BO_ \"GenMsgCycleTime\" INT 0 10000 ;";
let mut parser = Parser::new(input).unwrap();
let def = AttributeDefinition::parse(&mut parser).unwrap();
assert_eq!(def.name(), "GenMsgCycleTime");
assert_eq!(def.object_type(), AttributeObjectType::Message);
assert!(matches!(
def.value_type(),
AttributeValueType::Int { min: 0, max: 10000 }
));
}
#[test]
fn test_parse_ba_def_string() {
let input = b"\"BusType\" STRING ;";
let mut parser = Parser::new(input).unwrap();
let def = AttributeDefinition::parse(&mut parser).unwrap();
assert_eq!(def.name(), "BusType");
assert_eq!(def.object_type(), AttributeObjectType::Network);
assert!(matches!(def.value_type(), AttributeValueType::String));
}
#[test]
fn test_parse_ba_def_enum() {
let input = b"BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"J1939PG\" ;";
let mut parser = Parser::new(input).unwrap();
let def = AttributeDefinition::parse(&mut parser).unwrap();
assert_eq!(def.name(), "VFrameFormat");
assert_eq!(def.object_type(), AttributeObjectType::Message);
if let AttributeValueType::Enum { values } = def.value_type() {
assert_eq!(values.len(), 3);
assert_eq!(values.as_slice()[0].as_str(), "StandardCAN");
assert_eq!(values.as_slice()[1].as_str(), "ExtendedCAN");
assert_eq!(values.as_slice()[2].as_str(), "J1939PG");
} else {
panic!("Expected Enum type");
}
}
#[test]
fn test_parse_ba_def_def_int() {
let input = b"\"GenMsgCycleTime\" 100 ;";
let mut parser = Parser::new(input).unwrap();
let (name, value) = parse_attribute_default(&mut parser).unwrap();
assert_eq!(name.as_str(), "GenMsgCycleTime");
assert_eq!(value.as_int(), Some(100));
}
#[test]
fn test_parse_ba_def_def_string() {
let input = b"\"BusType\" \"CAN\" ;";
let mut parser = Parser::new(input).unwrap();
let (name, value) = parse_attribute_default(&mut parser).unwrap();
assert_eq!(name.as_str(), "BusType");
assert_eq!(value.as_string(), Some("CAN"));
}
#[test]
fn test_parse_ba_message() {
let input = b"\"GenMsgCycleTime\" BO_ 256 50 ;";
let mut parser = Parser::new(input).unwrap();
let (name, target, value) = parse_attribute_assignment(&mut parser).unwrap();
assert_eq!(name.as_str(), "GenMsgCycleTime");
assert!(matches!(target, AttributeTarget::Message(256)));
assert_eq!(value.as_int(), Some(50));
}
#[test]
fn test_parse_ba_signal() {
let input = b"\"GenSigStartValue\" SG_ 256 RPM 0.0 ;";
let mut parser = Parser::new(input).unwrap();
let (name, target, value) = parse_attribute_assignment(&mut parser).unwrap();
assert_eq!(name.as_str(), "GenSigStartValue");
if let AttributeTarget::Signal(msg_id, sig_name) = target {
assert_eq!(msg_id, 256);
assert_eq!(sig_name.as_str(), "RPM");
} else {
panic!("Expected Signal target");
}
assert_eq!(value.as_float(), Some(0.0));
}
#[test]
fn test_parse_ba_network() {
let input = b"\"BusType\" \"CAN\" ;";
let mut parser = Parser::new(input).unwrap();
let (name, target, value) = parse_attribute_assignment(&mut parser).unwrap();
assert_eq!(name.as_str(), "BusType");
assert!(matches!(target, AttributeTarget::Network));
assert_eq!(value.as_string(), Some("CAN"));
}
}