use regex::Regex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::proto::mavlink_messages_v1 as proto;
use crate::protocol::Value;
use super::super::errors::ProtoImportError;
use super::errors::MessageFieldParsingError;
const VALUE_AS_INVALID_REGEX: &str = r"^((-?\d+)(\.\d+)?)$";
const FIRST_LIST_VALUE_AS_INVALID_REGEX: &str = r"^\[((-?\d+)(\.\d+)?)\]$";
const FIRST_LIST_MAX_VALUE_AS_INVALID_REGEX: &str = r"^\[(.+_MAX)\]$";
const ENUM_ENTRY_VALUE_AS_INVALID_REGEX: &str = r"^[A-Z]+[A-Z_]+[A-Z]+$";
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MessageFieldInvalidValue {
Value(Value),
AllItems(Value),
FirstItem(Value),
EnumEntryValue(String),
}
impl MessageFieldInvalidValue {
pub fn parse(s: &str) -> Result<MessageFieldInvalidValue, MessageFieldParsingError> {
let normalized = s.trim();
let first_list_value_re = Regex::new(FIRST_LIST_VALUE_AS_INVALID_REGEX).unwrap();
let list_max_re = Regex::new(FIRST_LIST_MAX_VALUE_AS_INVALID_REGEX).unwrap();
let value_re = Regex::new(VALUE_AS_INVALID_REGEX).unwrap();
let enum_entry_name_re = Regex::new(ENUM_ENTRY_VALUE_AS_INVALID_REGEX).unwrap();
Ok(match normalized {
"NaN" => MessageFieldInvalidValue::Value(Value::FloatValue(f64::NAN)),
"NAN" => MessageFieldInvalidValue::Value(Value::FloatValue(f64::NAN)),
"[NaN:]" => MessageFieldInvalidValue::FirstItem(Value::FloatValue(f64::NAN)),
"[NaN]" => MessageFieldInvalidValue::AllItems(Value::FloatValue(f64::NAN)),
_ if list_max_re.is_match(normalized) => {
MessageFieldInvalidValue::FirstItem(Value::MaxValue)
}
_ if normalized.ends_with("_MAX") => MessageFieldInvalidValue::Value(Value::MaxValue),
_ if normalized.starts_with("0x") => {
let radix: u32 = (u32::try_from(normalized.chars().count()).unwrap() - 2) * 4;
MessageFieldInvalidValue::Value(Value::IntValue(
i128::from_str_radix(normalized.trim_start_matches("0x"), radix)
.map_err(MessageFieldParsingError::InvalidValueSpecIntParseError)?,
))
}
_ if value_re.is_match(normalized) => {
let captures = value_re.captures(normalized).unwrap();
if captures.get(3).is_none() {
MessageFieldInvalidValue::Value(Value::IntValue(
normalized
.parse::<i128>()
.map_err(MessageFieldParsingError::InvalidValueSpecIntParseError)?,
))
} else {
MessageFieldInvalidValue::Value(Value::FloatValue(
normalized
.parse::<f64>()
.map_err(MessageFieldParsingError::InvalidValueSpecFloatParseError)?,
))
}
}
_ if first_list_value_re.is_match(normalized) => {
let captures = first_list_value_re.captures(normalized).unwrap();
let value = captures.get(1).unwrap().as_str();
if captures.get(3).is_none() {
MessageFieldInvalidValue::AllItems(Value::IntValue(
value
.parse::<i128>()
.map_err(MessageFieldParsingError::InvalidValueSpecIntParseError)?,
))
} else {
MessageFieldInvalidValue::AllItems(Value::FloatValue(
value
.parse::<f64>()
.map_err(MessageFieldParsingError::InvalidValueSpecFloatParseError)?,
))
}
}
_ if enum_entry_name_re.is_match(normalized) => {
MessageFieldInvalidValue::EnumEntryValue(normalized.to_string())
}
&_ => {
return Err(MessageFieldParsingError::InvalidValueSpecParseError(
normalized.to_string(),
))
}
})
}
pub fn to_proto(&self) -> proto::MessageFieldInvalidValue {
use proto::message_field_invalid_value as spec;
proto::MessageFieldInvalidValue {
invalid: Some(match self {
MessageFieldInvalidValue::Value(value) => spec::Invalid::Value(value.to_proto()),
MessageFieldInvalidValue::AllItems(value) => {
spec::Invalid::AllItems(value.to_proto())
}
MessageFieldInvalidValue::FirstItem(value) => {
spec::Invalid::FirstItem(value.to_proto())
}
MessageFieldInvalidValue::EnumEntryValue(value) => {
spec::Invalid::EnumEntryValue(spec::EnumEntryValue {
name: value.clone(),
})
}
}),
}
}
pub fn from_proto(proto: &proto::MessageFieldInvalidValue) -> Result<Self, ProtoImportError> {
use crate::proto::mavlink_messages_v1::message_field_invalid_value as spec;
Ok(match &proto.invalid {
Some(spec::Invalid::Value(value)) => Self::Value(Value::from_proto(value.clone())?),
Some(spec::Invalid::AllItems(value)) => {
Self::AllItems(Value::from_proto(value.clone())?)
}
Some(spec::Invalid::FirstItem(value)) => {
Self::FirstItem(Value::from_proto(value.clone())?)
}
Some(spec::Invalid::EnumEntryValue(val)) => Self::EnumEntryValue(val.name.clone()),
None => return Err(ProtoImportError::MessageFieldInvalidValueValueIsNone),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use regex::Regex;
#[test]
fn first_list_value_as_invalid_pattern_is_correct() {
let re = Regex::new(FIRST_LIST_VALUE_AS_INVALID_REGEX).unwrap();
assert!(re.is_match("[1234]"));
assert!(re.is_match("[-1234]"));
assert!(re.is_match("[12.34]"));
assert!(re.is_match("[-12.34]"));
{
let captures = re.captures("[-1234]").unwrap();
assert_eq!(captures.get(1).unwrap().as_str(), "-1234");
assert_eq!(captures.get(2).unwrap().as_str(), "-1234");
assert_eq!(captures.get(3), None);
assert_eq!(captures.len(), 4);
}
{
let captures = re.captures("[-12.34]").unwrap();
assert_eq!(captures.get(1).unwrap().as_str(), "-12.34");
assert_eq!(captures.get(2).unwrap().as_str(), "-12");
assert_eq!(captures.get(3).unwrap().as_str(), ".34");
assert_eq!(captures.len(), 4);
}
}
#[test]
fn message_field_invalid_value_parser_is_correct() {
assert!(matches!(
MessageFieldInvalidValue::parse("INT16_MAX").unwrap(),
MessageFieldInvalidValue::Value(Value::MaxValue),
));
assert!(matches!(
MessageFieldInvalidValue::parse("-1234").unwrap(),
MessageFieldInvalidValue::Value(Value::IntValue(-1234)),
));
assert!(matches!(
MessageFieldInvalidValue::parse("[-1234]").unwrap(),
MessageFieldInvalidValue::AllItems(Value::IntValue(-1234)),
));
{
let parsed = MessageFieldInvalidValue::parse("[NaN]").unwrap();
if let MessageFieldInvalidValue::AllItems(Value::FloatValue(value)) = parsed {
assert!(value.is_nan());
} else {
panic!("Invalid parsing result: {:?}", parsed);
}
}
{
let parsed = MessageFieldInvalidValue::parse("[-12.34]").unwrap();
if let MessageFieldInvalidValue::AllItems(Value::FloatValue(value)) = parsed {
assert_eq!(value, -12.34);
} else {
panic!("Invalid parsing result: {:?}", parsed);
}
}
}
#[test]
fn conversion_to_proto_basics_are_correct() {
use proto::message_field_invalid_value as spec;
{
let expected = proto::MessageFieldInvalidValue {
invalid: Some(spec::Invalid::Value(proto::Value {
value: Some(proto::value::Value::FloatValue(proto::value::FloatValue {
value: Some(proto::value::float_value::Value::Nan(proto::value::NaN {})),
})),
})),
};
let converted = MessageFieldInvalidValue::Value(Value::FloatValue(f64::NAN)).to_proto();
if let proto::MessageFieldInvalidValue {
invalid:
Some(spec::Invalid::Value(proto::Value {
value:
Some(proto::value::Value::FloatValue(proto::value::FloatValue {
value:
Some(proto::value::float_value::Value::Nan(proto::value::NaN {})),
})),
})),
} = converted
{
} else {
panic!("Invalid conversion to proto. Expected: {expected:?}. Got: {converted:?}.")
}
}
{
let expected = proto::MessageFieldInvalidValue {
invalid: Some(spec::Invalid::Value(proto::Value {
value: Some(proto::value::Value::IntValue(-12)),
})),
};
let converted = MessageFieldInvalidValue::Value(Value::IntValue(-12)).to_proto();
if let proto::MessageFieldInvalidValue {
invalid:
Some(spec::Invalid::Value(proto::Value {
value: Some(proto::value::Value::IntValue(-12)),
})),
} = converted
{
} else {
panic!("Invalid conversion to proto. Expected: {expected:?}. Got: {converted:?}.")
}
}
{
let expected = proto::MessageFieldInvalidValue {
invalid: Some(spec::Invalid::Value(proto::Value {
value: Some(proto::value::Value::FloatValue(proto::value::FloatValue {
value: Some(proto::value::float_value::Value::Data(1.2)),
})),
})),
};
let converted = MessageFieldInvalidValue::Value(Value::FloatValue(1.2)).to_proto();
if let proto::MessageFieldInvalidValue {
invalid:
Some(spec::Invalid::Value(proto::Value {
value:
Some(proto::value::Value::FloatValue(proto::value::FloatValue {
value: Some(proto::value::float_value::Value::Data(val)),
})),
})),
} = converted
{
if val != 1.2 {
panic!(
"Invalid float value during conversion to proto. Expected: {expected:?}. Got: {converted:?}."
)
}
} else {
panic!("Invalid conversion to proto. Expected: {expected:?}. Got: {converted:?}.")
}
}
}
#[test]
fn conversion_to_proto_is_reversible() {
{
let original = MessageFieldInvalidValue::Value(Value::FloatValue(f64::NAN));
let reversed = MessageFieldInvalidValue::from_proto(&original.to_proto()).unwrap();
if let MessageFieldInvalidValue::Value(Value::FloatValue(val)) = reversed {
assert!(val.is_nan());
} else {
panic!(
"Message was damaged during reverse. Expected: {original:?}. Got: {reversed:?}"
);
}
}
{
let original = MessageFieldInvalidValue::Value(Value::IntValue(-12));
let reversed = MessageFieldInvalidValue::from_proto(&original.to_proto()).unwrap();
if let MessageFieldInvalidValue::Value(Value::IntValue(-12)) = reversed {
} else {
panic!(
"Message was damaged during reverse. Expected: {original:?}. Got: {reversed:?}"
);
}
}
{
let value: f64 = 1.2;
let original = MessageFieldInvalidValue::Value(Value::FloatValue(value));
let reversed = MessageFieldInvalidValue::from_proto(&original.to_proto()).unwrap();
if let MessageFieldInvalidValue::Value(Value::FloatValue(val)) = reversed {
if val != value {
panic!(
"Message float value was damaged during reverse. Expected: {original:?}. Got: {reversed:?}"
);
}
} else {
panic!(
"Message was damaged during reverse. Expected: {original:?}. Got: {reversed:?}"
);
}
}
}
}