cloudreve_api/
client.rs

1//! Unified Cloudreve client with automatic version detection
2
3use crate::api::{ApiVersion, VersionInfo};
4use crate::api::v3::ApiV3Client;
5use crate::api::v4::ApiV4Client as ApiV4ClientInner;
6use crate::Error;
7use log::debug;
8
9/// Unified Cloudreve client that automatically handles version differences
10pub enum UnifiedClient {
11    V3(ApiV3Client),
12    V4(ApiV4ClientInner),
13}
14
15impl UnifiedClient {
16    /// Create a new client with automatic version detection
17    pub async fn new(base_url: &str) -> Result<Self, Error> {
18        Self::with_version(base_url, None).await
19    }
20
21    /// Create a new client with explicit version or auto-detection
22    pub async fn with_version(base_url: &str, version: Option<ApiVersion>) -> Result<Self, Error> {
23        let base_url = base_url.trim_end_matches('/');
24
25        match version {
26            Some(ApiVersion::V3) => {
27                debug!("Creating V3 client for {}", base_url);
28                Ok(UnifiedClient::V3(ApiV3Client::new(base_url)))
29            }
30            Some(ApiVersion::V4) => {
31                debug!("Creating V4 client for {}", base_url);
32                Ok(UnifiedClient::V4(ApiV4ClientInner::new(base_url)))
33            }
34            None => {
35                // Auto-detect version
36                debug!("Auto-detecting API version for {}", base_url);
37                Self::detect_version(base_url).await
38            }
39        }
40    }
41
42    /// Detect the API version by trying endpoints
43    async fn detect_version(base_url: &str) -> Result<Self, Error> {
44        let base_url = base_url.trim_end_matches('/');
45
46        // Try V4 first (newer version)
47        debug!("Trying V4 endpoint...");
48        let v4_client = ApiV4ClientInner::new(base_url);
49        match v4_client.ping().await {
50            Ok(_) => {
51                debug!("V4 endpoint available, using V4 client");
52                return Ok(UnifiedClient::V4(v4_client));
53            }
54            Err(e) => {
55                debug!("V4 endpoint failed: {}", e);
56            }
57        }
58
59        // Try V3
60        debug!("Trying V3 endpoint...");
61        let v3_client = ApiV3Client::new(base_url);
62        match v3_client.ping().await {
63            Ok(_) => {
64                debug!("V3 endpoint available, using V3 client");
65                Ok(UnifiedClient::V3(v3_client))
66            }
67            Err(e) => {
68                debug!("V3 endpoint failed: {}", e);
69                Err(Error::InvalidResponse(
70                    "Could not detect API version. Neither V3 nor V4 endpoints responded.".to_string()
71                ))
72            }
73        }
74    }
75
76    /// Get version information
77    pub async fn get_version(&self) -> Result<VersionInfo, Error> {
78        match self {
79            UnifiedClient::V3(client) => client.get_version().await,
80            UnifiedClient::V4(client) => client.get_version().await,
81        }
82    }
83
84    /// Get the API version
85    pub fn api_version(&self) -> ApiVersion {
86        match self {
87            UnifiedClient::V3(_) => ApiVersion::V3,
88            UnifiedClient::V4(_) => ApiVersion::V4,
89        }
90    }
91
92    /// Get the base URL
93    pub fn base_url(&self) -> &str {
94        match self {
95            UnifiedClient::V3(client) => &client.base_url,
96            UnifiedClient::V4(client) => &client.base_url,
97        }
98    }
99
100    /// Check if the client is using V3
101    pub fn is_v3(&self) -> bool {
102        matches!(self, UnifiedClient::V3(_))
103    }
104
105    /// Check if the client is using V4
106    pub fn is_v4(&self) -> bool {
107        matches!(self, UnifiedClient::V4(_))
108    }
109
110    /// Get V3 client reference if applicable
111    pub fn as_v3(&self) -> Option<&ApiV3Client> {
112        match self {
113            UnifiedClient::V3(client) => Some(client),
114            _ => None,
115        }
116    }
117
118    /// Get V4 client reference if applicable
119    pub fn as_v4(&self) -> Option<&ApiV4ClientInner> {
120        match self {
121            UnifiedClient::V4(client) => Some(client),
122            _ => None,
123        }
124    }
125
126    /// Get mutable V3 client reference if applicable
127    pub fn as_v3_mut(&mut self) -> Option<&mut ApiV3Client> {
128        match self {
129            UnifiedClient::V3(client) => Some(client),
130            _ => None,
131        }
132    }
133
134    /// Get mutable V4 client reference if applicable
135    pub fn as_v4_mut(&mut self) -> Option<&mut ApiV4ClientInner> {
136        match self {
137            UnifiedClient::V4(client) => Some(client),
138            _ => None,
139        }
140    }
141}
142
143// Implement Clone for the unified client
144impl Clone for UnifiedClient {
145    fn clone(&self) -> Self {
146        match self {
147            UnifiedClient::V3(client) => UnifiedClient::V3(client.clone()),
148            UnifiedClient::V4(client) => UnifiedClient::V4(client.clone()),
149        }
150    }
151}
152
153impl std::fmt::Debug for UnifiedClient {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        match self {
156            UnifiedClient::V3(client) => f
157                .debug_tuple("UnifiedClient::V3")
158                .field(client)
159                .finish(),
160            UnifiedClient::V4(client) => f
161                .debug_tuple("UnifiedClient::V4")
162                .field(client)
163                .finish(),
164        }
165    }
166}