stakpak-gateway 0.3.73

Stakpak: Your DevOps AI Agent. Generate infrastructure code, debug Kubernetes, configure CI/CD, automate deployments, without giving an LLM the keys to production.
Documentation
use std::fmt;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct ChannelId(pub String);

impl fmt::Display for ChannelId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl From<&str> for ChannelId {
    fn from(value: &str) -> Self {
        Self(value.to_string())
    }
}

impl From<String> for ChannelId {
    fn from(value: String) -> Self {
        Self(value)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct PeerId(pub String);

impl fmt::Display for PeerId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl From<&str> for PeerId {
    fn from(value: &str) -> Self {
        Self(value.to_string())
    }
}

impl From<String> for PeerId {
    fn from(value: String) -> Self {
        Self(value)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ChatType {
    Direct,
    Group { id: String },
    Thread { group_id: String, thread_id: String },
}

#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct MediaAttachment {
    pub mime_type: String,
    pub data: Vec<u8>,
    pub filename: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InboundMessage {
    pub channel: ChannelId,
    pub peer_id: PeerId,
    pub chat_type: ChatType,
    pub text: String,
    pub media: Vec<MediaAttachment>,
    pub metadata: serde_json::Value,
    pub timestamp: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutboundReply {
    pub channel: ChannelId,
    pub peer_id: PeerId,
    pub chat_type: ChatType,
    pub text: String,
    pub metadata: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeliveryContext {
    pub channel: ChannelId,
    pub peer_id: PeerId,
    pub chat_type: ChatType,
    pub channel_meta: serde_json::Value,
    pub updated_at: i64,
}

#[cfg(test)]
mod tests {
    use serde_json::json;

    use super::ChatType;

    #[test]
    fn chat_type_direct_round_trip() {
        let chat_type = ChatType::Direct;

        let serialized = match serde_json::to_value(&chat_type) {
            Ok(value) => value,
            Err(error) => panic!("serialization failed: {error}"),
        };

        assert_eq!(serialized, json!({"kind": "direct"}));

        let deserialized: ChatType = match serde_json::from_value(serialized) {
            Ok(value) => value,
            Err(error) => panic!("deserialization failed: {error}"),
        };

        assert_eq!(deserialized, chat_type);
    }

    #[test]
    fn chat_type_group_round_trip() {
        let chat_type = ChatType::Group {
            id: "group-1".to_string(),
        };

        let serialized = match serde_json::to_value(&chat_type) {
            Ok(value) => value,
            Err(error) => panic!("serialization failed: {error}"),
        };

        assert_eq!(serialized, json!({"kind": "group", "id": "group-1"}));

        let deserialized: ChatType = match serde_json::from_value(serialized) {
            Ok(value) => value,
            Err(error) => panic!("deserialization failed: {error}"),
        };

        assert_eq!(deserialized, chat_type);
    }

    #[test]
    fn chat_type_thread_round_trip() {
        let chat_type = ChatType::Thread {
            group_id: "group-1".to_string(),
            thread_id: "thread-9".to_string(),
        };

        let serialized = match serde_json::to_value(&chat_type) {
            Ok(value) => value,
            Err(error) => panic!("serialization failed: {error}"),
        };

        assert_eq!(
            serialized,
            json!({"kind": "thread", "group_id": "group-1", "thread_id": "thread-9"})
        );

        let deserialized: ChatType = match serde_json::from_value(serialized) {
            Ok(value) => value,
            Err(error) => panic!("deserialization failed: {error}"),
        };

        assert_eq!(deserialized, chat_type);
    }
}