anyclaw-sdk-service 0.2.1

SDK for building anyclaw service extensions
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Parameters sent to a service during the `initialize` handshake.
// D-03 boundary: options values are service-defined, no fixed schema.
#[allow(clippy::disallowed_types)]
#[derive(Debug, Clone, Deserialize)]
pub struct ServiceInitializeParams {
    /// Deployment namespace for scoping Docker resources (networks, volumes).
    /// Derived from the Docker Compose project name or defaults to "anyclaw".
    pub namespace: String,
    /// Service-specific configuration options from `anyclaw.yaml`.
    #[serde(default)]
    pub options: HashMap<String, serde_json::Value>,
    /// Optional port hint from the supervisor.
    pub port: Option<u16>,
}

/// Result returned by a service after successful initialization.
// D-03 boundary: installation values are service-defined, no fixed schema.
#[allow(clippy::disallowed_types)]
#[derive(Debug, Clone, Serialize)]
pub struct ServiceInitializeResult {
    /// The port the service is listening on.
    pub port: u16,
    /// Installation metadata reported back to the supervisor.
    pub installation: HashMap<String, serde_json::Value>,
}

/// Health status reported by a service in response to a `health` request.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceHealthStatus {
    /// Current health state.
    pub status: HealthStatus,
    /// Optional human-readable message providing detail.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
}

/// Possible health states for a service.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HealthStatus {
    /// Service is operating normally.
    Healthy,
    /// Service is operational but experiencing issues.
    Degraded,
    /// Service is not operational.
    Unhealthy,
}

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

    #[rstest]
    fn when_params_deserialized_with_namespace_then_field_present() {
        let json = r#"{"namespace": "chome-dorey-6y7huf", "options": {}}"#;
        let params: ServiceInitializeParams = serde_json::from_str(json).unwrap();
        assert_eq!(params.namespace, "chome-dorey-6y7huf");
    }

    #[rstest]
    fn when_params_deserialized_with_all_fields_then_all_present() {
        let json = r#"{"namespace": "my-app", "options": {"key": "val"}, "port": 8080}"#;
        let params: ServiceInitializeParams = serde_json::from_str(json).unwrap();
        assert_eq!(params.namespace, "my-app");
        assert_eq!(params.port, Some(8080));
        assert!(params.options.contains_key("key"));
    }
}