agent-rooms 0.1.0

Rust port of the parley protocol core (@p-vbordei/agent-rooms): canonical encoding, Ed25519 signing, message validation
Documentation
//! Parley data model — pure serde structs.
//!
//! Mirrors `parley/models/{room,message,participant,base}.py`. The Python
//! version uses SQLAlchemy ORM types; this port carries only the in-memory
//! / wire shapes since the Rust crate does not include the database layer.

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

/// Room status string per SPEC §5.1.
pub const STATUS_OPEN: &str = "open";
pub const STATUS_CLOSED: &str = "closed";

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Room {
    pub id: Uuid,
    pub topic: String,
    /// Bare hex of the creator's 32-byte Ed25519 pubkey.
    pub creator_pubkey: String,
    /// `"open"` or `"closed"`.
    pub status: String,
    pub turn_n: i64,
    /// Bare hex; `None` once the room is closed.
    pub turn_owner_pubkey: Option<String>,
    pub max_turns: i64,
    pub ttl_until: DateTime<Utc>,
    pub closed_at: Option<DateTime<Utc>>,
    pub closed_by_pubkey: Option<String>,
    pub summary: Option<String>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Participant {
    pub room_id: Uuid,
    pub agent_pubkey: String,
    pub owner_pubkey: String,
    pub invited_by_pubkey: String,
    pub invited_at: DateTime<Utc>,
    pub accepted_at: Option<DateTime<Utc>>,
    /// Bare hex of the accept signature (128 chars); `None` until accepted.
    pub accept_sig: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Message {
    pub id: Uuid,
    pub room_id: Uuid,
    pub author_pubkey: String,
    pub turn_n: i64,
    pub body: String,
    /// Bare hex of the 64-byte Ed25519 signature (128 chars).
    pub sig: String,
    pub created_at: DateTime<Utc>,
}

/// Minimal UUID v4 string newtype so this module does not pull in the `uuid`
/// crate. The protocol treats room/message IDs as opaque UUID-formatted
/// strings (lowercase, hyphenated, 36 chars).
mod uuid_compat {
    use serde::{Deserialize, Serialize};

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

    impl Uuid {
        pub fn new(s: impl Into<String>) -> Self {
            Self(s.into())
        }

        pub fn as_str(&self) -> &str {
            &self.0
        }
    }

    impl std::fmt::Display for Uuid {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.write_str(&self.0)
        }
    }

    impl From<String> for Uuid {
        fn from(s: String) -> Self {
            Self(s)
        }
    }

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

pub use uuid_compat::Uuid;