use serde::{Deserialize, Serialize};
use crate::catalog::Component;
pub const PROTOCOL_VERSION: &str = "v0.10";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServerMessage {
pub version: String,
#[serde(flatten)]
pub content: ServerMessageContent,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ServerMessageContent {
CreateSurface(CreateSurface),
UpdateComponents(UpdateComponents),
UpdateDataModel(UpdateDataModel),
DeleteSurface(DeleteSurface),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CreateSurface {
pub surface_id: String,
pub catalog_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<Theme>,
#[serde(skip_serializing_if = "Option::is_none")]
pub send_data_model: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Theme {
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icon_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_display_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UpdateComponents {
pub surface_id: String,
pub components: Vec<Component>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UpdateDataModel {
pub surface_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DeleteSurface {
pub surface_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ClientMessage {
pub version: String,
#[serde(flatten)]
pub content: ClientMessageContent,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ClientMessageContent {
Action(ActionMessage),
Error(ErrorMessage),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ActionMessage {
pub name: String,
pub surface_id: String,
pub source_component_id: String,
pub timestamp: String,
pub context: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ErrorMessage {
pub code: ErrorCode,
pub surface_id: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ErrorCode {
#[serde(rename = "VALIDATION_FAILED")]
ValidationFailed,
#[serde(other)]
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ClientCapabilities {
pub supported_catalog_ids: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inline_catalogs: Option<Vec<Catalog>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Catalog {
pub catalog_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub components: Option<serde_json::Map<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub functions: Option<Vec<FunctionDefinition>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<serde_json::Map<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct FunctionDefinition {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub parameters: serde_json::Value,
pub return_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ClientDataModel {
pub version: String,
pub surfaces: std::collections::HashMap<String, serde_json::Value>,
}
impl ServerMessage {
pub fn new(content: ServerMessageContent) -> Self {
Self {
version: PROTOCOL_VERSION.to_string(),
content,
}
}
pub fn create_surface(surface_id: &str, catalog_id: &str) -> Self {
Self::new(ServerMessageContent::CreateSurface(CreateSurface {
surface_id: surface_id.to_string(),
catalog_id: catalog_id.to_string(),
theme: None,
send_data_model: None,
}))
}
pub fn update_components(surface_id: &str, components: Vec<Component>) -> Self {
Self::new(ServerMessageContent::UpdateComponents(UpdateComponents {
surface_id: surface_id.to_string(),
components,
}))
}
pub fn update_data_model(surface_id: &str, value: serde_json::Value) -> Self {
Self::new(ServerMessageContent::UpdateDataModel(UpdateDataModel {
surface_id: surface_id.to_string(),
path: None,
value: Some(value),
}))
}
pub fn delete_surface(surface_id: &str) -> Self {
Self::new(ServerMessageContent::DeleteSurface(DeleteSurface {
surface_id: surface_id.to_string(),
}))
}
}
impl ClientMessage {
pub fn new(content: ClientMessageContent) -> Self {
Self {
version: PROTOCOL_VERSION.to_string(),
content,
}
}
pub fn action(
surface_id: &str,
name: &str,
source_component_id: &str,
context: serde_json::Map<String, serde_json::Value>,
) -> Self {
Self::new(ClientMessageContent::Action(ActionMessage {
name: name.to_string(),
surface_id: surface_id.to_string(),
source_component_id: source_component_id.to_string(),
timestamp: chrono::Utc::now().to_rfc3339(),
context,
}))
}
pub fn validation_error(surface_id: &str, path: &str, message: &str) -> Self {
Self::new(ClientMessageContent::Error(ErrorMessage {
code: ErrorCode::ValidationFailed,
surface_id: surface_id.to_string(),
message: message.to_string(),
path: Some(path.to_string()),
}))
}
pub fn error(surface_id: &str, message: &str) -> Self {
Self::new(ClientMessageContent::Error(ErrorMessage {
code: ErrorCode::Other,
surface_id: surface_id.to_string(),
message: message.to_string(),
path: None,
}))
}
}
impl ClientCapabilities {
pub fn new(supported_catalog_ids: Vec<String>) -> Self {
Self {
supported_catalog_ids,
inline_catalogs: None,
}
}
pub fn with_inline_catalog(mut self, catalog: Catalog) -> Self {
self.inline_catalogs
.get_or_insert_with(Vec::new)
.push(catalog);
self
}
}
impl ClientDataModel {
pub fn new() -> Self {
Self {
version: PROTOCOL_VERSION.to_string(),
surfaces: std::collections::HashMap::new(),
}
}
pub fn with_surface(mut self, surface_id: &str, data: serde_json::Value) -> Self {
self.surfaces.insert(surface_id.to_string(), data);
self
}
}
impl Default for ClientDataModel {
fn default() -> Self {
Self::new()
}
}