mavinspect 0.1.0-alpha2

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

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

use super::message_field_invalid_value::MessageFieldInvalidValue;
use super::message_field_type::MessageFieldType;

/// MAVLink message field.
///
/// Used in [`crate::protocol::Message`].
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MessageField {
    name: String,
    description: String,
    r#type: MessageFieldType,
    r#enum: Option<String>,
    units: Option<Units>,
    bitmask: bool,
    print_format: Option<String>,
    default: Option<Value>,
    invalid: Option<MessageFieldInvalidValue>,
    instance: bool,
    extension: bool,
}

impl MessageField {
    /// Default constructor
    ///
    /// # Arguments
    ///
    /// * `name` - Message field name.
    /// * `description` - Message field description.
    /// * `type` - Message field type.
    /// * `enum` - Enum name which defines message values.
    /// * `units` - Units of measurement.
    /// * `bitmask` - Set to `true` for bitmask fields, default `false`.
    /// * `print_format` - Print format.
    ///    C `printf`-like format (i.e. `0x%04x`).
    /// * `default` - Specification for default value.
    /// * `invalid` - Defines how invalid values should be specified.
    ///    Specifies a value that can be set on a field to indicate that the data is invalid: the
    ///    recipient should ignore the field if it has this value. For example,
    ///    `BATTERY_STATUS.current_battery` specifies `invalid="-1"`, so a battery that does not
    ///    measure supplied current should set `BATTERY_STATUS.current_battery` to `-1`.
    /// * `instance` - Instance flag.
    ///    If `true`, this indicates that the message contains the information for a particular
    ///    sensor battery (e.g. Battery 1, Battery 2, etc.) and that this field indicates which
    ///    sensor. Default is `false`.
    /// * `extension` - Whether this message field is an extension.
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        name: String,
        description: String,
        r#type: MessageFieldType,
        r#enum: Option<String>,
        units: Option<Units>,
        bitmask: bool,
        print_format: Option<String>,
        default: Option<Value>,
        invalid: Option<MessageFieldInvalidValue>,
        instance: bool,
        extension: bool,
    ) -> Self {
        Self {
            name,
            description,
            r#type,
            r#enum,
            units,
            bitmask,
            print_format,
            default,
            invalid,
            instance,
            extension,
        }
    }

    /// Converts to Protobuf [`proto::MessageField`].
    pub fn to_proto(&self) -> proto::MessageField {
        proto::MessageField {
            name: self.name.clone(),
            description: self.description.clone(),
            r#type: Some(self.r#type.to_proto()),
            r#enum: self.r#enum.clone(),
            units: self.units.as_ref().map(|u| u.to_proto() as i32),
            bitmask: self.bitmask,
            print_format: self.print_format.clone(),
            default: self.default.as_ref().map(|val| val.to_proto()),
            invalid: self.invalid.as_ref().map(|i| i.to_proto()),
            instance: self.instance,
            extension: self.extension,
        }
    }

    /// Constructs from Protobuf [`proto::MessageField`].
    pub fn from_proto(proto: &proto::MessageField) -> Result<Self, ProtoImportError> {
        Ok(Self {
            name: proto.name.clone(),
            description: proto.description.clone(),
            r#type: match &proto.r#type {
                None => return Err(ProtoImportError::MessageFieldTypeIsNone),
                Some(fld_type) => MessageFieldType::from_proto(fld_type)?,
            },
            r#enum: proto.r#enum.clone(),
            units: match proto.units {
                None => None,
                Some(val) => Some(Units::from_value(val)?),
            },
            bitmask: proto.bitmask,
            print_format: proto.print_format.clone(),
            default: match &proto.default {
                None => None,
                Some(default) => Some(Value::from_proto(default.clone())?),
            },
            invalid: match &proto.invalid {
                None => None,
                Some(invalid) => Some(MessageFieldInvalidValue::from_proto(invalid)?),
            },
            instance: proto.instance,
            extension: proto.extension,
        })
    }

    /// Message field name
    pub fn name(&self) -> &String {
        &self.name
    }

    /// Message field description
    pub fn description(&self) -> &String {
        &self.description
    }

    /// Message field type
    pub fn r#type(&self) -> &MessageFieldType {
        &self.r#type
    }

    /// Enum name which defines message values.
    pub fn r#enum(&self) -> &Option<String> {
        &self.r#enum
    }

    /// Message field units
    pub fn units(&self) -> &Option<Units> {
        &self.units
    }

    // bitmask,
    //             print_format,
    //             default,

    /// Set to `true` for bitmask fields, default `false`.
    pub fn bitmask(&self) -> bool {
        self.bitmask
    }

    /// Print format.
    ///
    /// C `printf`-like format (i.e. `0x%04x`).
    pub fn print_format(&self) -> &Option<String> {
        &self.print_format
    }

    /// Specification for invalid field value (if applicable)
    pub fn invalid(&self) -> &Option<MessageFieldInvalidValue> {
        &self.invalid
    }

    /// Instance flag.
    ///
    /// If `true`, this indicates that the message contains the information for a particular sensor
    /// or battery (e.g. Battery 1, Battery 2, etc.) and that this field indicates which sensor.
    /// Default is `false`.
    pub fn instance(&self) -> bool {
        self.instance
    }

    /// Whether this message field is extension or not
    pub fn extension(&self) -> bool {
        self.extension
    }
}