mavinspect 0.1.0-alpha2

MAVInspect is a CLI tool and a library to parse and inspect MAVLink protocol XML definitions
Documentation
use regex::Regex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

const RE_INT_VALUE: &str = r"^-?[0-9]+$";
const RE_FLOAT_VALUE: &str = r"^-?[0-9]+\.[0-9]+$";

use crate::proto::mavlink_messages_v1 as proto;
use crate::protocol::errors::{ParseError, ProtoImportError};

/// Value specification.
///
/// Used in [`crate::protocol::MessageField`], [`crate::protocol::EnumEntryMavCmdParam`], and
/// [`crate::protocol::MessageFieldInvalidValue`].
///
/// See: [message](https://mavlink.io/en/guide/xml_schema.html#messages) section in MAVLink XML schema documentation.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Value {
    IntValue(i128),
    FloatValue(f64),
    MaxValue,
}

impl Value {
    /// Converts to Protobuf [`proto::Value`].
    pub fn to_proto(&self) -> proto::Value {
        proto::Value {
            value: Some(match self {
                Value::IntValue(val) => proto::value::Value::IntValue(*val as i64),
                Value::FloatValue(val) => {
                    proto::value::Value::FloatValue(proto::value::FloatValue {
                        value: Some(if val.is_nan() {
                            proto::value::float_value::Value::Nan(proto::value::NaN {})
                        } else {
                            proto::value::float_value::Value::Data(*val)
                        }),
                    })
                }
                Value::MaxValue => proto::value::Value::MaxValue(proto::value::Max {}),
            }),
        }
    }

    /// Constructs from Protobuf [`proto::Value`].
    pub fn from_proto(proto: proto::Value) -> Result<Self, ProtoImportError> {
        Ok(match proto.value {
            None => return Err(ProtoImportError::ValueValueIsNone),
            Some(value) => match value {
                proto::value::Value::IntValue(val) => Value::IntValue(val as i128),
                proto::value::Value::FloatValue(val) => match val.value {
                    None => return Err(ProtoImportError::ValueFloatValueIsNone),
                    Some(fvalue) => match fvalue {
                        proto::value::float_value::Value::Nan(_) => Value::FloatValue(f64::NAN),
                        proto::value::float_value::Value::Data(val) => Value::FloatValue(val),
                    },
                },
                proto::value::Value::MaxValue(_) => Value::MaxValue,
            },
        })
    }

    /// Parses value from string.
    ///
    /// First, it attempts to parse value as an integer, then reserves to floating point number.
    pub fn parse(str: &str) -> Result<Self, ParseError> {
        let re_int = Regex::new(RE_INT_VALUE).unwrap();
        let re_float = Regex::new(RE_FLOAT_VALUE).unwrap();

        Ok(match str {
            "NaN" => Self::FloatValue(f64::NAN),
            "NAN" => Self::FloatValue(f64::NAN),
            val if re_int.is_match(str) => {
                Self::IntValue(val.parse::<i128>().map_err(ParseError::ValueIntError)?)
            }
            val if re_float.is_match(str) => {
                Self::FloatValue(val.parse::<f64>().map_err(ParseError::ValueFloatError)?)
            }
            &_ => return Err(ParseError::InvalidValue(str.to_string())),
        })
    }
}