darq 0.1.0

darq CLI + TUI — autonomous issue → PR pipeline with SAT and a learning loop.
Documentation
//! Daemon IPC protocol types.
//!
//! Simple JSON lines protocol over Unix socket.
//! Request → Response pattern. Events use the request/response model with polling.

use serde::{Deserialize, Serialize};

// ── Request ──

/// A request from client to daemon.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
    /// Unique request ID (UUID). Responses carry the same ID.
    pub id: String,
    /// Method name.
    pub method: Method,
    /// Method parameters (varies by method).
    #[serde(default)]
    pub params: serde_json::Value,
}

/// Available daemon methods.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Method {
    /// Get daemon status and run counts.
    Status,
    /// List runs with optional filters.
    RunList,
    /// Show full state of a run.
    RunShow,
    /// Approve a run awaiting approval.
    RunApprove,
    /// Cancel a running run.
    RunCancel,
    /// Start a single workflow.
    WorkflowStart,
    /// Run a full workflow chain.
    WorkflowChain,
    /// Sweep a milestone (process all issues).
    Sweep,
    /// Subscribe to events (streaming — future work).
    Subscribe,
    /// Learning analytics and pattern effectiveness.
    Stats,
    /// Graceful shutdown.
    Shutdown,
}

// ── Response ──

/// A response from daemon to client.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Response {
    /// Successful response.
    Success {
        id: String,
        result: serde_json::Value,
    },
    /// Error response.
    Error { id: String, error: String },
}

impl Response {
    pub fn success(id: String, result: impl Serialize) -> Self {
        Self::Success {
            id,
            result: serde_json::to_value(result).unwrap_or(serde_json::Value::Null),
        }
    }

    pub fn error(id: String, error: impl Into<String>) -> Self {
        Self::Error {
            id,
            error: error.into(),
        }
    }
}

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

    #[test]
    fn test_request_serialization() {
        let req = Request {
            id: "abc-123".into(),
            method: Method::Status,
            params: serde_json::Value::Object(Default::default()),
        };
        let json = serde_json::to_string(&req).unwrap();
        assert!(json.contains("\"method\":\"status\""));
        assert!(json.contains("\"id\":\"abc-123\""));

        let parsed: Request = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed.method, Method::Status);
        assert_eq!(parsed.id, "abc-123");
    }

    #[test]
    fn test_response_success() {
        let resp = Response::success("abc-123".into(), serde_json::json!({"running": 2}));
        let json = serde_json::to_string(&resp).unwrap();
        assert!(json.contains("\"type\":\"success\""));
        assert!(json.contains("\"running\":2"));

        let parsed: Response = serde_json::from_str(&json).unwrap();
        match parsed {
            Response::Success { id, result } => {
                assert_eq!(id, "abc-123");
                assert_eq!(result["running"], 2);
            }
            _ => panic!("expected Success"),
        }
    }

    #[test]
    fn test_response_error() {
        let resp = Response::error("abc-123".into(), "not found");
        let json = serde_json::to_string(&resp).unwrap();
        assert!(json.contains("\"type\":\"error\""));
        assert!(json.contains("\"error\":\"not found\""));
    }

    #[test]
    fn test_all_methods_serialize() {
        let methods = [
            (Method::Status, "status"),
            (Method::RunList, "run_list"),
            (Method::RunShow, "run_show"),
            (Method::RunApprove, "run_approve"),
            (Method::RunCancel, "run_cancel"),
            (Method::WorkflowStart, "workflow_start"),
            (Method::WorkflowChain, "workflow_chain"),
            (Method::Sweep, "sweep"),
            (Method::Stats, "stats"),
            (Method::Subscribe, "subscribe"),
            (Method::Shutdown, "shutdown"),
        ];
        for (method, expected) in methods {
            let json = serde_json::to_string(&method).unwrap();
            assert_eq!(json, format!("\"{}\"", expected), "Method {:?}", method);
        }
    }
}