cloudreve-api 0.9.0

A Rust library for interacting with Cloudreve API
Documentation
//! Common types for Cloudreve API v4

use crate::Error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Business code returned when a batch operation only partially succeeded.
///
/// The server aggregates per-item failures into `aggregated_error` and reports
/// this code at the top level. Items missing from that map succeeded.
pub const CODE_BATCH_OPERATION_NOT_FULLY_COMPLETED: i32 = 40081;

/// Business code returned when a target file is locked by another session.
///
/// At the top level the lock details land in `data`; inside `aggregated_error`
/// they land in the sub-item's own `data`.
pub const CODE_LOCK_CONFLICT: i32 = 40073;

/// A single item's failure inside [`ApiResponse::aggregated_error`].
///
/// The server serializes each failed item as a full response object, so a
/// sub-item carries its own business code and payload — a lock conflict, for
/// example, arrives as `code: 40073` with the unlock tokens in `data`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregatedItemError {
    pub code: i32,
    #[serde(default)]
    pub msg: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub data: Option<serde_json::Value>,
}

/// Generic API response wrapper
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiResponse<T> {
    pub code: i32,
    pub msg: String,
    pub data: Option<T>,
    /// Per-item failures for batch operations, keyed by the URI that was sent.
    ///
    /// Only present when the operation partially failed (see
    /// [`CODE_BATCH_OPERATION_NOT_FULLY_COMPLETED`]). Items that succeeded are
    /// absent from the map, so the successful set is the request's URIs minus
    /// these keys.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub aggregated_error: Option<HashMap<String, AggregatedItemError>>,
}

impl ApiResponse<serde_json::Value> {
    /// Convert a non-zero response into the most informative error variant.
    ///
    /// Folding every failure into [`Error::Api`] would discard exactly what
    /// callers need to act on: which items of a batch failed, and the unlock
    /// tokens behind a lock conflict. Endpoints that can return either should
    /// route their errors through here.
    pub fn into_error(self) -> Error {
        if let Some(errors) = self.aggregated_error {
            return Error::Aggregate {
                code: self.code,
                message: self.msg,
                errors,
            };
        }
        match self.data {
            Some(data) if !data.is_null() => Error::ApiWithData {
                code: self.code,
                message: self.msg,
                data,
            },
            _ => Error::Api {
                code: self.code,
                message: self.msg,
            },
        }
    }
}