infraqueue-lib 0.1.0

Core library for INFRAQUEUE
Documentation
use chrono::Utc; // use UTC for consistent, timezone-agnostic timestamps
use serde::{Deserialize, Serialize};
use uuid::Uuid; // for unique message IDs

/// Message envelope for INFRAQUEUE.
///
/// LLM usage: payload is typically a JSON string carrying inputs for generation,
/// embedding, or RAG retrieval. Suggested fields: `v` (schema version), `trace_id`,
/// `prompt` or `query`, `params` (e.g., temperature, top_k), and domain-specific context.
/// Keep payloads reasonably small; store large artifacts in object storage and reference
/// them by URL or key.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InfraQueueMessage {
    pub id: String,           // globally unique identifier for the message
    pub timestamp: u64,       // timestamp in milliseconds since UNIX epoch (UTC)
    pub sender: String,       // identifier for the message producer
    pub topic: String,        // logical topic/queue name
    pub payload: String,      // opaque payload (typically JSON)
    pub retry_count: u8,      // number of delivery retries attempted
    pub priority: Option<u8>, // optional priority (lower is higher priority)
}

impl InfraQueueMessage {
    /// Create a new message with safe defaults and robust timestamp handling.
    ///
    /// LLM guidance:
    /// - Use this to wrap a job payload for topics like `rag-search`, `semantic-search`,
    ///   `content-tagging`, `embeddings`, `pii-scrubbing`, etc.
    /// - Payload is a JSON string; include a schema version (e.g., `v: 1`) so you can evolve it.
    /// - Keep payloads small; reference large documents via URLs/keys.
    /// - Idempotency: include a trace or business key so your worker can de-duplicate.
    ///
    /// Example payloads (stringified JSON):
    /// - RAG search:
    ///   {"v":1,"trace_id":"abc123","query":"how do I rotate haproxy certs?","top_k":5,"filters":{"env":"prod"}}
    /// - Generation:
    ///   {"v":1,"trace_id":"abc123","prompt":"Write a changelog entry for...","params":{"temperature":0.2}}
    /// - Embedding:
    ///   {"v":1,"trace_id":"abc123","text":"The quick brown fox...","model":"text-embedding-3-small"}
    pub fn new(
        sender: impl Into<String>,
        topic: impl Into<String>,
        payload: impl Into<String>,
    ) -> Self {
        // Get milliseconds since epoch; can be negative on systems with incorrect clock,
        // so clamp to zero before casting to u64 to avoid underflow.
        let ts_ms_i64 = Utc::now().timestamp_millis();
        let ts_ms_u64 = if ts_ms_i64 >= 0 { ts_ms_i64 as u64 } else { 0 };

        // Generate a random UUIDv4 as the message ID.
        let id = Uuid::new_v4().to_string();

        Self {
            id,
            timestamp: ts_ms_u64,
            sender: sender.into(),
            topic: topic.into(),
            payload: payload.into(),
            retry_count: 0,
            priority: None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_message_new_basics() {
        let m = InfraQueueMessage::new("s", "t", "p");
        assert!(!m.id.is_empty());
        assert!(m.timestamp > 0);
        assert_eq!(m.sender, "s");
        assert_eq!(m.topic, "t");
        assert_eq!(m.payload, "p");
        assert_eq!(m.retry_count, 0);
        assert!(m.priority.is_none());
    }
}