modeldriveprotocol-client 2.2.0

Rust client SDK for Model Drive Protocol.
Documentation
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

use crate::error::MdpClientError;
use crate::models::{AuthContext, ClientDescriptor, HttpMethod, SerializedError};
use crate::path_utils::is_concrete_path;

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(tag = "type")]
pub enum ClientToServerMessage {
    #[serde(rename = "registerClient")]
    RegisterClient {
        client: ClientDescriptor,
        #[serde(skip_serializing_if = "Option::is_none")]
        auth: Option<AuthContext>,
    },
    #[serde(rename = "updateClientCatalog")]
    UpdateClientCatalog {
        #[serde(rename = "clientId")]
        client_id: String,
        paths: Vec<crate::models::PathDescriptor>,
    },
    #[serde(rename = "unregisterClient")]
    UnregisterClient {
        #[serde(rename = "clientId")]
        client_id: String,
    },
    #[serde(rename = "callClientResult")]
    CallClientResult {
        #[serde(rename = "requestId")]
        request_id: String,
        ok: bool,
        #[serde(skip_serializing_if = "Option::is_none")]
        data: Option<Value>,
        #[serde(skip_serializing_if = "Option::is_none")]
        error: Option<SerializedError>,
    },
    #[serde(rename = "pong")]
    Pong { timestamp: i64 },
}

#[derive(Clone, Debug, PartialEq)]
pub enum ServerToClientMessage {
    CallClient(CallClientRequest),
    Ping { timestamp: i64 },
    Pong { timestamp: i64 },
}

#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallClientRequest {
    pub request_id: String,
    pub client_id: String,
    pub method: HttpMethod,
    pub path: String,
    pub params: Option<Map<String, Value>>,
    pub query: Option<Map<String, Value>>,
    pub body: Option<Value>,
    pub headers: Option<std::collections::HashMap<String, String>>,
    pub auth: Option<AuthContext>,
}

impl ServerToClientMessage {
    pub fn from_text(raw: &str) -> Result<Self, MdpClientError> {
        let value: Value = serde_json::from_str(raw)?;
        Self::from_value(value)
    }

    pub fn from_value(value: Value) -> Result<Self, MdpClientError> {
        let object = value
            .as_object()
            .ok_or_else(|| MdpClientError::Protocol("invalid MDP message payload".to_string()))?;

        let message_type = object
            .get("type")
            .and_then(Value::as_str)
            .ok_or_else(|| MdpClientError::Protocol("missing message type".to_string()))?;

        match message_type {
            "ping" => Ok(Self::Ping {
                timestamp: object
                    .get("timestamp")
                    .and_then(Value::as_i64)
                    .ok_or_else(|| MdpClientError::Protocol("invalid ping payload".to_string()))?,
            }),
            "pong" => Ok(Self::Pong {
                timestamp: object
                    .get("timestamp")
                    .and_then(Value::as_i64)
                    .ok_or_else(|| MdpClientError::Protocol("invalid pong payload".to_string()))?,
            }),
            "callClient" => {
                let request: CallClientRequest = serde_json::from_value(value)?;
                if !is_concrete_path(&request.path) {
                    return Err(MdpClientError::Protocol("invalid callClient path".to_string()));
                }
                Ok(Self::CallClient(request))
            }
            other => Err(MdpClientError::Protocol(format!(
                "unsupported server message type `{other}`"
            ))),
        }
    }
}