mavinspect 0.1.0-alpha2

MAVInspect is a CLI tool and a library to parse and inspect MAVLink protocol XML definitions
Documentation
use std::collections::HashMap;
use tonic::{Request, Response, Status};

use crate::proto::service as proto;

/// Autogenerated gRPC server that serves [`MavInspectService`].
///
/// Service implemented in [`MavInspectServerImplementation`].
pub use proto::mav_inspect_service_server::MavInspectServiceServer;

use proto::mav_inspect_service_server::MavInspectService;
use proto::{
    GetDialectRequest, GetDialectResponse, GetProtocolRequest, GetProtocolResponse,
    ListDialectsRequest, ListDialectsResponse,
};

use crate::proto::mavlink_messages_v1::{Dialect, Protocol};
use crate::proto::service::{
    EnumSummary, GetDialectSummaryRequest, GetDialectSummaryResponse, GetEnumRequest,
    GetEnumResponse, GetMessageRequest, GetMessageResponse, ListDialectEnumsRequest,
    ListDialectEnumsResponse, ListDialectMessagesRequest, ListDialectMessagesResponse,
    MessageSummary,
};

/// MavInspect gRPC service server implementation.
///
/// Implements [`MavInspectService`] and encapsulates MAVLink [`Protocol`] definition.
#[derive(Default)]
pub struct MavInspectServerImplementation {
    protocol: Protocol,
}

impl MavInspectServerImplementation {
    /// Default constructor
    pub fn new(protocol: Protocol) -> Self {
        Self { protocol }
    }

    fn dialect_names(&self) -> Vec<String> {
        self.protocol
            .dialects
            .keys()
            .cloned()
            .collect::<Vec<String>>()
    }

    fn dialect_message_summaries(dialect: &Dialect) -> HashMap<u32, MessageSummary> {
        dialect
            .messages
            .iter()
            .map(|(&id, msg)| {
                (
                    id,
                    MessageSummary {
                        id: msg.id,
                        name: msg.name.clone(),
                        description: msg.description.clone(),
                        wip: msg.wip,
                        defined_in: msg.defined_in.clone(),
                    },
                )
            })
            .collect()
    }

    fn dialect_enum_summaries(dialect: &Dialect) -> HashMap<String, EnumSummary> {
        dialect
            .enums
            .iter()
            .map(|(name, enm)| {
                (
                    name.clone(),
                    EnumSummary {
                        name: enm.name.clone(),
                        description: enm.description.clone(),
                        bitmask: enm.bitmask,
                        defined_in: enm.defined_in.clone(),
                    },
                )
            })
            .collect()
    }
}

#[tonic::async_trait]
impl MavInspectService for MavInspectServerImplementation {
    async fn get_protocol(
        &self,
        request: Request<GetProtocolRequest>,
    ) -> Result<Response<GetProtocolResponse>, Status> {
        println!("Got GetProtocolRequest from {:?}", request.remote_addr());

        let reply = GetProtocolResponse {
            protocol: Some(self.protocol.clone()),
        };
        Ok(Response::new(reply))
    }

    async fn list_dialects(
        &self,
        request: Request<ListDialectsRequest>,
    ) -> Result<Response<ListDialectsResponse>, Status> {
        println!("Got ListDialectsRequest from {:?}", request.remote_addr());

        let reply = ListDialectsResponse {
            dialects: self.dialect_names(),
        };
        Ok(Response::new(reply))
    }

    async fn get_dialect(
        &self,
        request: Request<GetDialectRequest>,
    ) -> Result<Response<GetDialectResponse>, Status> {
        println!("Got GetDialectRequest from {:?}", request.remote_addr());

        let name = request.into_inner().name;

        match self.protocol.dialects.get(&name) {
            None => Err(Status::not_found(
                format!(
                    "Dialect \"{name}\" is not present in protocol. Available options: {:?}",
                    self.dialect_names()
                )
                .as_str()
                .to_string(),
            )),
            Some(dialect) => Ok(Response::new(GetDialectResponse {
                dialect: Some(dialect.clone()),
            })),
        }
    }

    async fn get_dialect_summary(
        &self,
        request: Request<GetDialectSummaryRequest>,
    ) -> Result<Response<GetDialectSummaryResponse>, Status> {
        println!(
            "Got GetDialectSummaryRequest from {:?}",
            request.remote_addr()
        );

        let name = request.into_inner().name;

        match self.protocol.dialects.get(&name) {
            None => Err(Status::not_found(
                format!(
                    "Dialect \"{name}\" is not present in protocol. Available options: {:?}",
                    self.dialect_names()
                )
                .as_str()
                .to_string(),
            )),
            Some(dialect) => Ok(Response::new(GetDialectSummaryResponse {
                name,
                version: dialect.version,
                dialect: dialect.dialect,
                messages: MavInspectServerImplementation::dialect_message_summaries(dialect),
                enums: MavInspectServerImplementation::dialect_enum_summaries(dialect),
            })),
        }
    }

    async fn list_dialect_messages(
        &self,
        request: Request<ListDialectMessagesRequest>,
    ) -> Result<Response<ListDialectMessagesResponse>, Status> {
        println!(
            "Got ListDialectMessagesRequest from {:?}",
            request.remote_addr()
        );

        let name = request.into_inner().dialect_name;

        match self.protocol.dialects.get(&name) {
            None => Err(Status::not_found(
                format!(
                    "Dialect \"{name}\" is not present in protocol. Available options: {:?}",
                    self.dialect_names()
                )
                .as_str()
                .to_string(),
            )),
            Some(dialect) => Ok(Response::new(ListDialectMessagesResponse {
                messages: MavInspectServerImplementation::dialect_message_summaries(dialect),
            })),
        }
    }

    async fn get_message(
        &self,
        request: Request<GetMessageRequest>,
    ) -> Result<Response<GetMessageResponse>, Status> {
        println!("Got GetMessageRequest from {:?}", request.remote_addr());

        let req = request.into_inner();
        let dialect_name = req.dialect_name;
        let message_id = req.message_id;

        match self.protocol.dialects.get(&dialect_name) {
            None => Err(Status::not_found(
                format!(
                    "Dialect \"{dialect_name}\" is not present in protocol. Available options: {:?}",
                    self.dialect_names()
                )
                    .as_str()
                    .to_string(),
            )),
            Some(dialect) => match dialect.messages.get(&message_id) {
                None => Err(Status::not_found(
                    format!(
                        "Dialect \"{dialect_name}\" does not have message with ID={message_id}."
                    )
                        .as_str()
                        .to_string(),
                )),
                Some(message) => Ok(Response::new(GetMessageResponse {
                    message: Some(message.clone()),
                })),
            },
        }
    }

    async fn list_dialect_enums(
        &self,
        request: Request<ListDialectEnumsRequest>,
    ) -> Result<Response<ListDialectEnumsResponse>, Status> {
        println!(
            "Got ListDialectEnumsRequest from {:?}",
            request.remote_addr()
        );

        let name = request.into_inner().dialect_name;

        match self.protocol.dialects.get(&name) {
            None => Err(Status::not_found(
                format!(
                    "Dialect \"{name}\" is not present in protocol. Available options: {:?}",
                    self.dialect_names()
                )
                .as_str()
                .to_string(),
            )),
            Some(dialect) => Ok(Response::new(ListDialectEnumsResponse {
                enums: MavInspectServerImplementation::dialect_enum_summaries(dialect),
            })),
        }
    }

    async fn get_enum(
        &self,
        request: Request<GetEnumRequest>,
    ) -> Result<Response<GetEnumResponse>, Status> {
        println!("Got GetMessageRequest from {:?}", request.remote_addr());

        let req = request.into_inner();
        let dialect_name = req.dialect_name;
        let enum_name = req.enum_name;

        match self.protocol.dialects.get(&dialect_name) {
            None => Err(Status::not_found(
                format!(
                    "Dialect \"{dialect_name}\" is not present in protocol. Available options: {:?}",
                    self.dialect_names()
                )
                    .as_str()
                    .to_string(),
            )),
            Some(dialect) => match dialect.enums.get(&enum_name) {
                None => Err(Status::not_found(
                    format!(
                        "Dialect \"{dialect_name}\" does not have enum named \"{enum_name}\"."
                    )
                        .as_str()
                        .to_string(),
                )),
                Some(enm) => Ok(Response::new(GetEnumResponse {
                    r#enum: Some(enm.clone()),
                })),
            },
        }
    }
}