dink-sdk 0.3.1

Rust SDK for Dink edge mesh platform — JSON-over-NATS RPC for IoT and edge computing
Documentation
use serde::{Deserialize, Serialize};

use crate::error::{DinkError, Result};

/// Platform (management) response envelope.
/// `{"success": true, "data": <T>}` or `{"success": false, "error": {...}}`
#[derive(Debug, Deserialize)]
pub struct PlatformResponse<T> {
    pub success: bool,
    pub data: Option<T>,
    pub error: Option<ErrorInfo>,
}

/// Service (edge→caller) response envelope.
/// `{"result": <T>, "success": true}` or `{"success": false, "error": {...}}`
#[derive(Debug, Deserialize)]
pub struct ServiceResponse<T> {
    pub success: Option<bool>,
    pub result: Option<T>,
    pub error: Option<ErrorInfo>,
}

/// Error detail included in both platform and service error responses.
#[derive(Debug, Deserialize, Clone)]
pub struct ErrorInfo {
    pub code: String,
    pub message: String,
}

/// Stream initiation request sent to a `.stream` subject.
/// `{"reply_subject": "<inbox>", "data": <req>}`
#[derive(Debug, Serialize)]
pub struct StreamRequest<T: Serialize> {
    pub reply_subject: String,
    pub data: T,
}

/// Ack returned by the edge when it begins streaming.
/// `{"status": "streaming", "cancelSubject": "..."}`
#[derive(Debug, Deserialize)]
pub struct StreamAck {
    pub status: Option<String>,
    #[serde(rename = "cancelSubject")]
    pub cancel_subject: Option<String>,
}

/// Parse a platform (management) response envelope, extracting the `data` field on success.
pub fn parse_platform_response<T: serde::de::DeserializeOwned>(data: &[u8]) -> Result<T> {
    let resp: PlatformResponse<T> = serde_json::from_slice(data)?;
    if resp.success {
        resp.data
            .ok_or_else(|| DinkError::Nats("empty response data".into()))
    } else {
        let err = resp.error.unwrap_or(ErrorInfo {
            code: "UNKNOWN".into(),
            message: "unknown error".into(),
        });
        Err(DinkError::Service {
            service: String::new(),
            method: String::new(),
            code: err.code,
            message: err.message,
        })
    }
}

/// Parse a service (edge) response envelope, extracting the `result` field on success.
///
/// Falls back to deserializing the raw bytes as `T` when neither `result` nor an explicit
/// error is present (some services return bare JSON).
pub fn parse_service_response<T: serde::de::DeserializeOwned>(data: &[u8]) -> Result<T> {
    let resp: ServiceResponse<T> = serde_json::from_slice(data)?;
    if let Some(false) = resp.success {
        let err = resp.error.unwrap_or(ErrorInfo {
            code: "UNKNOWN".into(),
            message: "unknown error".into(),
        });
        return Err(DinkError::Service {
            service: String::new(),
            method: String::new(),
            code: err.code,
            message: err.message,
        });
    }
    if let Some(result) = resp.result {
        return Ok(result);
    }
    // Fallback: try parsing raw bytes as T (bare JSON response).
    serde_json::from_slice(data).map_err(Into::into)
}