spiral-sdk 0.1.11

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 spiral_contract::error::{ErrorEnvelope, ServiceError};
use spiral_contract::strict::{DeserializeStrict, Strict};
use uuid::Uuid;

/// Query-local aliases for legacy keyed Table IDs, addressed as legacy.alias. Requires
/// defaultProjectId; every Table must belong to that Project and be readable by the caller. At most
/// 16 aliases are allowed. Each Table is resolved once per query; snapshots across Tables are not
/// atomic.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct LegacyTables(pub BTreeMap<String, String>);
impl<'de> DeserializeStrict<'de> for LegacyTables {
    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        Ok(Self(
            Strict::<BTreeMap<String, String>>::deserialize(deserializer)?.0,
        ))
    }
}

/// Positional SQL parameters as one complete Arrow IPC stream, base64-encoded with the standard
/// alphabet and padding. The stream contains exactly one row; column positions bind $1 through $N,
/// and column names are ignored. Arrow types and validity carry types and nulls without JSON
/// numeric coercion. Missing, extra or unsupported parameters fail before execution. Omit this
/// field for statements without parameters.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Parameters(pub String);
impl std::fmt::Display for Parameters {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(formatter)
    }
}
impl<'de> DeserializeStrict<'de> for Parameters {
    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        Ok(Self(Strict::<String>::deserialize(deserializer)?.0))
    }
}

/// One read-only DataFusion statement, including a read-only EXPLAIN. defaultProjectId supplies the
/// Project for unqualified resource names; without it, resource names use "project-id"."path@ref".
/// With a default Project, "path@ref" is sufficient. Queries without Project resources need no
/// Project context. defaultRef is a branch or c-prefixed Project commit, defaulting to main; an
/// explicit @ref on a resource overrides it. Each Project and branch pair resolves once to an
/// immutable commit for this request. Explicit commit and version refs remain exact. Different
/// references may select different snapshots; there is no atomic snapshot across Projects. The host
/// forwards the caller's API credential to the owning services when resolving names and obtaining
/// storage bindings. Those services enforce resource permissions and delegated scope caps. Defaults
/// grant no resource authority and do not limit the Projects a query can reference. Writes, DDL,
/// COPY, session mutations and multiple statements are rejected before execution. SPQL functions
/// use the host's ScanDAL implementation.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Query {
    /// Contract field defaultProjectId.
    #[serde(rename = "defaultProjectId")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub default_project_id: Option<String>,
    /// Contract field defaultRef.
    #[serde(rename = "defaultRef")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub default_ref: Option<String>,
    /// Contract field legacyTables.
    #[serde(rename = "legacyTables")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub legacy_tables: Option<LegacyTables>,
    /// Contract field parameters.
    #[serde(rename = "parameters")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Parameters>,
    /// Contract field sql.
    #[serde(rename = "sql")]
    pub sql: String,
}
impl<'de> DeserializeStrict<'de> for Query {
    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(deny_unknown_fields)]
        struct __Wire {
            #[serde(rename = "defaultProjectId")]
            #[serde(default)]
            default_project_id: Strict<Option<String>>,
            #[serde(rename = "defaultRef")]
            #[serde(default)]
            default_ref: Strict<Option<String>>,
            #[serde(rename = "legacyTables")]
            #[serde(default)]
            legacy_tables: Strict<Option<LegacyTables>>,
            #[serde(rename = "parameters")]
            #[serde(default)]
            parameters: Strict<Option<Parameters>>,
            #[serde(rename = "sql")]
            sql: Strict<String>,
        }
        let wire = __Wire::deserialize(deserializer)?;
        Ok(Self {
            default_project_id: wire.default_project_id.0,
            default_ref: wire.default_ref.0,
            legacy_tables: wire.legacy_tables.0,
            parameters: wire.parameters.0,
            sql: wire.sql.0,
        })
    }
}

/// Typed errors declared by this service contract.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SqlError {
    /// Sql:Internal.
    Internal { message: String },
    /// Sql:InvalidArgument.
    InvalidArgument { message: String },
    /// Sql:NotFound.
    NotFound { resource: String },
    /// Sql:PermissionDenied.
    PermissionDenied { resource: String },
    /// Sql:RequestTooLarge.
    RequestTooLarge { message: String },
    /// Sql:Timeout.
    Timeout { message: String },
    /// Sql:Unauthenticated.
    Unauthenticated { message: String },
    /// Sql:Unavailable.
    Unavailable { message: String },
}
impl SqlError {
    /// HTTP status for this declared error.
    pub fn status(&self) -> u16 {
        match self {
            Self::Internal { .. } => 500,
            Self::InvalidArgument { .. } => 400,
            Self::NotFound { .. } => 404,
            Self::PermissionDenied { .. } => 403,
            Self::RequestTooLarge { .. } => 413,
            Self::Timeout { .. } => 504,
            Self::Unauthenticated { .. } => 401,
            Self::Unavailable { .. } => 503,
        }
    }
    /// Serialize only contract-declared error parameters.
    pub fn envelope(&self, instance: String) -> ErrorEnvelope {
        match self {
            Self::Internal { message } => {
                let mut envelope = ErrorEnvelope::new("INTERNAL", "Sql:Internal", instance);
                envelope
                    .parameters
                    .insert("message".into(), message.to_string());
                envelope
            }
            Self::InvalidArgument { message } => {
                let mut envelope =
                    ErrorEnvelope::new("INVALID_ARGUMENT", "Sql:InvalidArgument", instance);
                envelope
                    .parameters
                    .insert("message".into(), message.to_string());
                envelope
            }
            Self::NotFound { resource } => {
                let mut envelope = ErrorEnvelope::new("NOT_FOUND", "Sql:NotFound", instance);
                envelope
                    .parameters
                    .insert("resource".into(), resource.to_string());
                envelope
            }
            Self::PermissionDenied { resource } => {
                let mut envelope =
                    ErrorEnvelope::new("PERMISSION_DENIED", "Sql:PermissionDenied", instance);
                envelope
                    .parameters
                    .insert("resource".into(), resource.to_string());
                envelope
            }
            Self::RequestTooLarge { message } => {
                let mut envelope =
                    ErrorEnvelope::new("REQUEST_ENTITY_TOO_LARGE", "Sql:RequestTooLarge", instance);
                envelope
                    .parameters
                    .insert("message".into(), message.to_string());
                envelope
            }
            Self::Timeout { message } => {
                let mut envelope = ErrorEnvelope::new("TIMEOUT", "Sql:Timeout", instance);
                envelope
                    .parameters
                    .insert("message".into(), message.to_string());
                envelope
            }
            Self::Unauthenticated { message } => {
                let mut envelope =
                    ErrorEnvelope::new("UNAUTHENTICATED", "Sql:Unauthenticated", instance);
                envelope
                    .parameters
                    .insert("message".into(), message.to_string());
                envelope
            }
            Self::Unavailable { message } => {
                let mut envelope = ErrorEnvelope::new("UNAVAILABLE", "Sql:Unavailable", instance);
                envelope
                    .parameters
                    .insert("message".into(), message.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() {
            "Sql:Internal" => {
                if envelope.error_code != "INTERNAL" {
                    return None;
                }
                let message = envelope.parameters.get("message")?.clone();
                Some(Self::Internal { message })
            }
            "Sql:InvalidArgument" => {
                if envelope.error_code != "INVALID_ARGUMENT" {
                    return None;
                }
                let message = envelope.parameters.get("message")?.clone();
                Some(Self::InvalidArgument { message })
            }
            "Sql:NotFound" => {
                if envelope.error_code != "NOT_FOUND" {
                    return None;
                }
                let resource = envelope.parameters.get("resource")?.clone();
                Some(Self::NotFound { resource })
            }
            "Sql:PermissionDenied" => {
                if envelope.error_code != "PERMISSION_DENIED" {
                    return None;
                }
                let resource = envelope.parameters.get("resource")?.clone();
                Some(Self::PermissionDenied { resource })
            }
            "Sql:RequestTooLarge" => {
                if envelope.error_code != "REQUEST_ENTITY_TOO_LARGE" {
                    return None;
                }
                let message = envelope.parameters.get("message")?.clone();
                Some(Self::RequestTooLarge { message })
            }
            "Sql:Timeout" => {
                if envelope.error_code != "TIMEOUT" {
                    return None;
                }
                let message = envelope.parameters.get("message")?.clone();
                Some(Self::Timeout { message })
            }
            "Sql:Unauthenticated" => {
                if envelope.error_code != "UNAUTHENTICATED" {
                    return None;
                }
                let message = envelope.parameters.get("message")?.clone();
                Some(Self::Unauthenticated { message })
            }
            "Sql:Unavailable" => {
                if envelope.error_code != "UNAVAILABLE" {
                    return None;
                }
                let message = envelope.parameters.get("message")?.clone();
                Some(Self::Unavailable { message })
            }
            _ => None,
        }
    }
}
impl Display for SqlError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Internal { message } => write!(formatter, "Sql:Internal (message: {})", message),
            Self::InvalidArgument { message } => {
                write!(formatter, "Sql:InvalidArgument (message: {})", message)
            }
            Self::NotFound { resource } => {
                write!(formatter, "Sql:NotFound (resource: {})", resource)
            }
            Self::PermissionDenied { resource } => {
                write!(formatter, "Sql:PermissionDenied (resource: {})", resource)
            }
            Self::RequestTooLarge { message } => {
                write!(formatter, "Sql:RequestTooLarge (message: {})", message)
            }
            Self::Timeout { message } => write!(formatter, "Sql:Timeout (message: {})", message),
            Self::Unauthenticated { message } => {
                write!(formatter, "Sql:Unauthenticated (message: {})", message)
            }
            Self::Unavailable { message } => {
                write!(formatter, "Sql:Unavailable (message: {})", message)
            }
        }
    }
}
impl StdError for SqlError {}
impl ServiceError for SqlError {
    fn to_envelope(&self, instance: String) -> ErrorEnvelope {
        Self::envelope(self, instance)
    }
    fn from_envelope(envelope: &ErrorEnvelope) -> Option<Self> {
        Self::decode_envelope(envelope)
    }
}