cloudreve_api/api/v4/models/common.rs
1//! Common types for Cloudreve API v4
2
3use crate::Error;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// Business code returned when a batch operation only partially succeeded.
8///
9/// The server aggregates per-item failures into `aggregated_error` and reports
10/// this code at the top level. Items missing from that map succeeded.
11pub const CODE_BATCH_OPERATION_NOT_FULLY_COMPLETED: i32 = 40081;
12
13/// Business code returned when a target file is locked by another session.
14///
15/// At the top level the lock details land in `data`; inside `aggregated_error`
16/// they land in the sub-item's own `data`.
17pub const CODE_LOCK_CONFLICT: i32 = 40073;
18
19/// A single item's failure inside [`ApiResponse::aggregated_error`].
20///
21/// The server serializes each failed item as a full response object, so a
22/// sub-item carries its own business code and payload — a lock conflict, for
23/// example, arrives as `code: 40073` with the unlock tokens in `data`.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct AggregatedItemError {
26 pub code: i32,
27 #[serde(default)]
28 pub msg: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub data: Option<serde_json::Value>,
31}
32
33/// Generic API response wrapper
34#[derive(Debug, Serialize, Deserialize)]
35pub struct ApiResponse<T> {
36 pub code: i32,
37 pub msg: String,
38 pub data: Option<T>,
39 /// Per-item failures for batch operations, keyed by the URI that was sent.
40 ///
41 /// Only present when the operation partially failed (see
42 /// [`CODE_BATCH_OPERATION_NOT_FULLY_COMPLETED`]). Items that succeeded are
43 /// absent from the map, so the successful set is the request's URIs minus
44 /// these keys.
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub aggregated_error: Option<HashMap<String, AggregatedItemError>>,
47}
48
49impl ApiResponse<serde_json::Value> {
50 /// Convert a non-zero response into the most informative error variant.
51 ///
52 /// Folding every failure into [`Error::Api`] would discard exactly what
53 /// callers need to act on: which items of a batch failed, and the unlock
54 /// tokens behind a lock conflict. Endpoints that can return either should
55 /// route their errors through here.
56 pub fn into_error(self) -> Error {
57 if let Some(errors) = self.aggregated_error {
58 return Error::Aggregate {
59 code: self.code,
60 message: self.msg,
61 errors,
62 };
63 }
64 match self.data {
65 Some(data) if !data.is_null() => Error::ApiWithData {
66 code: self.code,
67 message: self.msg,
68 data,
69 },
70 _ => Error::Api {
71 code: self.code,
72 message: self.msg,
73 },
74 }
75 }
76}