tcvectordb_rust/
client.rs

1use reqwest::{Client, Response};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::time::Duration;
5use url::Url;
6
7use crate::database::Database;
8use crate::enums::ReadConsistency;
9use crate::error::{Result, VectorDBError};
10
11#[derive(Debug, Clone)]
12pub struct VectorDBClient {
13    client: Client,
14    base_url: Url,
15    username: String,
16    key: String,
17    read_consistency: ReadConsistency,
18}
19
20#[derive(Debug, Serialize)]
21#[allow(dead_code)]
22struct AuthRequest {
23    username: String,
24    key: String,
25}
26
27#[derive(Debug, Deserialize)]
28#[allow(dead_code)]
29struct AuthResponse {
30    code: i32,
31    msg: String,
32    #[serde(default)]
33    token: Option<String>,
34}
35
36#[derive(Debug, Deserialize)]
37struct ApiResponse<T> {
38    code: i32,
39    msg: String,
40    #[serde(default)]
41    data: Option<T>,
42}
43
44impl VectorDBClient {
45    /// Create a new VectorDB client
46    pub fn new(
47        url: impl AsRef<str>,
48        username: impl Into<String>,
49        key: impl Into<String>,
50        read_consistency: ReadConsistency,
51        timeout_secs: u64,
52    ) -> Result<Self> {
53        let base_url = Url::parse(url.as_ref())?;
54        let client = Client::builder()
55            .timeout(Duration::from_secs(timeout_secs))
56            .build()?;
57
58        Ok(Self {
59            client,
60            base_url,
61            username: username.into(),
62            key: key.into(),
63            read_consistency,
64        })
65    }
66
67    /// Create a new VectorDB client with custom reqwest client
68    pub fn with_client(
69        client: Client,
70        url: impl AsRef<str>,
71        username: impl Into<String>,
72        key: impl Into<String>,
73        read_consistency: ReadConsistency,
74    ) -> Result<Self> {
75        let base_url = Url::parse(url.as_ref())?;
76
77        Ok(Self {
78            client,
79            base_url,
80            username: username.into(),
81            key: key.into(),
82            read_consistency,
83        })
84    }
85
86    /// Get the HTTP client
87    pub fn http_client(&self) -> &Client {
88        &self.client
89    }
90
91    /// Get the base URL
92    pub fn base_url(&self) -> &Url {
93        &self.base_url
94    }
95
96    /// Get the username
97    pub fn username(&self) -> &str {
98        &self.username
99    }
100
101    /// Get the API key
102    pub fn key(&self) -> &str {
103        &self.key
104    }
105
106    /// Get the read consistency setting
107    pub fn read_consistency(&self) -> &ReadConsistency {
108        &self.read_consistency
109    }
110
111    /// Check if a database exists
112    pub async fn exists_database(&self, database_name: &str) -> Result<bool> {
113        let databases = self.list_databases().await?;
114        Ok(databases.iter().any(|db| db.name() == database_name))
115    }
116
117    /// Create a new database
118    pub async fn create_database(&self, database_name: impl Into<String>) -> Result<Database> {
119        let database_name = database_name.into();
120        let db = Database::new(self.clone(), database_name.clone());
121        db.create().await?;
122        Ok(db)
123    }
124
125    /// Create a database if it doesn't exist
126    pub async fn create_database_if_not_exists(&self, database_name: impl Into<String>) -> Result<Database> {
127        let database_name = database_name.into();
128        if self.exists_database(&database_name).await? {
129            Ok(Database::new(self.clone(), database_name))
130        } else {
131            self.create_database(database_name).await
132        }
133    }
134
135    /// Drop a database
136    pub async fn drop_database(&self, database_name: impl Into<String>) -> Result<Value> {
137        let database_name = database_name.into();
138        let db = Database::new(self.clone(), database_name);
139        db.drop().await
140    }
141
142    /// List all databases
143    pub async fn list_databases(&self) -> Result<Vec<Database>> {
144        let url = self.base_url.join("/database/list")?;
145        let response = self.client
146            .get(url)
147            .header("Authorization", format!("Bearer {}", self.key))
148            .send()
149            .await?;
150
151        let api_response: ApiResponse<Value> = self.handle_response(response).await?;
152        
153        if api_response.code != 0 {
154            return Err(VectorDBError::server_error(api_response.code, api_response.msg));
155        }
156
157        let databases_data = api_response.data.unwrap_or(Value::Array(vec![]));
158        let database_names: Vec<String> = serde_json::from_value(databases_data)?;
159        
160        Ok(database_names
161            .into_iter()
162            .map(|name| Database::new(self.clone(), name))
163            .collect())
164    }
165
166    /// Get a database by name
167    pub async fn database(&self, database_name: impl Into<String>) -> Result<Database> {
168        let database_name = database_name.into();
169        if self.exists_database(&database_name).await? {
170            Ok(Database::new(self.clone(), database_name))
171        } else {
172            Err(VectorDBError::param_error(
173                14100,
174                format!("Database not exist: {}", database_name),
175            ))
176        }
177    }
178
179    /// Internal method to handle HTTP responses
180    pub(crate) async fn handle_response<T>(&self, response: Response) -> Result<T>
181    where
182        T: for<'de> Deserialize<'de>,
183    {
184        let status = response.status();
185        let text = response.text().await?;
186
187        if !status.is_success() {
188            return Err(VectorDBError::connect_error(
189                status.as_u16() as i32,
190                format!("HTTP error {}: {}", status, text),
191            ));
192        }
193
194        serde_json::from_str(&text).map_err(|e| {
195            VectorDBError::unexpected_error(format!("Failed to parse response: {}", e))
196        })
197    }
198
199    /// Internal method to make authenticated requests
200    pub(crate) async fn request(&self, method: reqwest::Method, path: &str) -> Result<reqwest::RequestBuilder> {
201        let url = self.base_url.join(path)?;
202        Ok(self.client
203            .request(method, url)
204            .header("Authorization", format!("Bearer {}", self.key))
205            .header("Content-Type", "application/json"))
206    }
207
208    /// Internal method to make GET requests
209    pub(crate) async fn get(&self, path: &str) -> Result<reqwest::RequestBuilder> {
210        self.request(reqwest::Method::GET, path).await
211    }
212
213    /// Internal method to make POST requests
214    pub(crate) async fn post(&self, path: &str) -> Result<reqwest::RequestBuilder> {
215        self.request(reqwest::Method::POST, path).await
216    }
217
218    /// Internal method to make PUT requests
219    #[allow(dead_code)]
220    pub(crate) async fn put(&self, path: &str) -> Result<reqwest::RequestBuilder> {
221        self.request(reqwest::Method::PUT, path).await
222    }
223
224    /// Internal method to make DELETE requests
225    pub(crate) async fn delete(&self, path: &str) -> Result<reqwest::RequestBuilder> {
226        self.request(reqwest::Method::DELETE, path).await
227    }
228}