spiral-sdk 0.1.9

Rust SDK for Spiral resource and table APIs
Documentation
// Generated by tools/service-contracts/rust.py. Do not edit.
#![doc = "Generated transport-independent contract objects."]

use std::collections::BTreeMap;
use std::error::Error as StdError;
use std::fmt::{self, Display, Formatter};

use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use spiral_contract::error::{ErrorEnvelope, ServiceError};
use spiral_contract::strict::{DeserializeStrict, Strict};
use uuid::Uuid;

/// Events scoped to exactly one legacy table or project. The service rejects requests that provide
/// both scope identifiers or neither identifier.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReportUsageRequest {
    /// Contract field events.
    #[serde(rename = "events")]
    pub events: Vec<UsageEvent>,
    /// Contract field projectId.
    #[serde(rename = "projectId")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub project_id: Option<String>,
    /// Contract field tableId.
    #[serde(rename = "tableId")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub table_id: Option<String>,
}
impl<'de> DeserializeStrict<'de> for ReportUsageRequest {
    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(deny_unknown_fields)]
        struct __Wire {
            #[serde(rename = "events")]
            events: Strict<Vec<UsageEvent>>,
            #[serde(rename = "projectId")]
            #[serde(default)]
            project_id: Strict<Option<String>>,
            #[serde(rename = "tableId")]
            #[serde(default)]
            table_id: Strict<Option<String>>,
        }
        let wire = __Wire::deserialize(deserializer)?;
        Ok(Self {
            events: wire.events.0,
            project_id: wire.project_id.0,
            table_id: wire.table_id.0,
        })
    }
}

/// One schema-agnostic metering event. The server overwrites project_id and org_id with
/// authoritative values before delivery.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UsageEvent {
    /// Contract field eventType.
    #[serde(rename = "eventType")]
    pub event_type: String,
    /// Contract field properties.
    #[serde(rename = "properties")]
    pub properties: BTreeMap<String, Value>,
}
impl<'de> DeserializeStrict<'de> for UsageEvent {
    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(deny_unknown_fields)]
        struct __Wire {
            #[serde(rename = "eventType")]
            event_type: Strict<String>,
            #[serde(rename = "properties")]
            properties: Strict<BTreeMap<String, Value>>,
        }
        let wire = __Wire::deserialize(deserializer)?;
        Ok(Self {
            event_type: wire.event_type.0,
            properties: wire.properties.0,
        })
    }
}

/// Typed errors declared by this service contract.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UsageError {
    /// Usage:Conflict.
    Conflict { reason: String },
    /// Usage:InvalidArgument.
    InvalidArgument { reason: String },
    /// Usage:NotFound.
    NotFound { resource: String },
}
impl UsageError {
    /// HTTP status for this declared error.
    pub fn status(&self) -> u16 {
        match self {
            Self::Conflict { .. } => 409,
            Self::InvalidArgument { .. } => 400,
            Self::NotFound { .. } => 404,
        }
    }
    /// Serialize only contract-declared error parameters.
    pub fn envelope(&self, instance: String) -> ErrorEnvelope {
        match self {
            Self::Conflict { reason } => {
                let mut envelope = ErrorEnvelope::new("CONFLICT", "Usage:Conflict", instance);
                envelope
                    .parameters
                    .insert("reason".into(), reason.to_string());
                envelope
            }
            Self::InvalidArgument { reason } => {
                let mut envelope =
                    ErrorEnvelope::new("INVALID_ARGUMENT", "Usage:InvalidArgument", instance);
                envelope
                    .parameters
                    .insert("reason".into(), reason.to_string());
                envelope
            }
            Self::NotFound { resource } => {
                let mut envelope = ErrorEnvelope::new("NOT_FOUND", "Usage:NotFound", instance);
                envelope
                    .parameters
                    .insert("resource".into(), resource.to_string());
                envelope
            }
        }
    }
    /// Decode a declared error and its typed error parameters.
    pub fn decode_envelope(envelope: &ErrorEnvelope) -> Option<Self> {
        if envelope.error_instance_id.len() != 36
            || Uuid::parse_str(&envelope.error_instance_id).is_err()
        {
            return None;
        }
        match envelope.error_name.as_str() {
            "Usage:Conflict" => {
                if envelope.error_code != "CONFLICT" {
                    return None;
                }
                let reason = envelope.parameters.get("reason")?.clone();
                Some(Self::Conflict { reason })
            }
            "Usage:InvalidArgument" => {
                if envelope.error_code != "INVALID_ARGUMENT" {
                    return None;
                }
                let reason = envelope.parameters.get("reason")?.clone();
                Some(Self::InvalidArgument { reason })
            }
            "Usage:NotFound" => {
                if envelope.error_code != "NOT_FOUND" {
                    return None;
                }
                let resource = envelope.parameters.get("resource")?.clone();
                Some(Self::NotFound { resource })
            }
            _ => None,
        }
    }
}
impl Display for UsageError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Conflict { reason } => write!(formatter, "Usage:Conflict (reason: {})", reason),
            Self::InvalidArgument { reason } => {
                write!(formatter, "Usage:InvalidArgument (reason: {})", reason)
            }
            Self::NotFound { resource } => {
                write!(formatter, "Usage:NotFound (resource: {})", resource)
            }
        }
    }
}
impl StdError for UsageError {}
impl ServiceError for UsageError {
    fn to_envelope(&self, instance: String) -> ErrorEnvelope {
        Self::envelope(self, instance)
    }
    fn from_envelope(envelope: &ErrorEnvelope) -> Option<Self> {
        Self::decode_envelope(envelope)
    }
}