1mod link;
7mod read;
8mod remove;
9mod update;
10mod write;
11
12use std::sync::Arc;
13
14use reqwest::Method;
15
16use reduct_base::error::ErrorCode;
17use reduct_base::msg::bucket_api::{BucketInfo, BucketSettings, FullBucketInfo, QuotaType};
18use reduct_base::msg::entry_api::{EntryInfo, RenameEntry};
19
20use crate::client::Result;
21use crate::http_client::HttpClient;
22
23pub struct Bucket {
25 pub(crate) name: String,
26 pub(crate) http_client: Arc<HttpClient>,
27}
28
29pub struct BucketBuilder {
30 name: String,
31 exist_ok: bool,
32 settings: BucketSettings,
33 http_client: Arc<HttpClient>,
34}
35
36impl BucketBuilder {
37 pub(crate) fn new(name: String, http_client: Arc<HttpClient>) -> Self {
38 Self {
39 name,
40 exist_ok: false,
41 settings: BucketSettings::default(),
42 http_client,
43 }
44 }
45
46 pub fn exist_ok(mut self, exist_ok: bool) -> Self {
48 self.exist_ok = exist_ok;
49 self
50 }
51
52 pub fn quota_type(mut self, quota_type: QuotaType) -> Self {
54 self.settings.quota_type = Some(quota_type);
55 self
56 }
57
58 pub fn quota_size(mut self, quota_size: u64) -> Self {
60 self.settings.quota_size = Some(quota_size);
61 self
62 }
63
64 pub fn max_block_size(mut self, max_block_size: u64) -> Self {
66 self.settings.max_block_size = Some(max_block_size);
67 self
68 }
69
70 pub fn max_block_records(mut self, max_block_records: u64) -> Self {
72 self.settings.max_block_records = Some(max_block_records);
73 self
74 }
75
76 pub fn settings(mut self, settings: BucketSettings) -> Self {
78 self.settings = settings;
79 self
80 }
81
82 pub async fn send(self) -> Result<Bucket> {
84 let result = self
85 .http_client
86 .send_json(Method::POST, &format!("/b/{}", self.name), self.settings)
87 .await;
88 match result {
89 Ok(_) => {}
90 Err(e) => {
91 if !(self.exist_ok && e.status() == ErrorCode::Conflict) {
92 return Err(e);
93 }
94 }
95 }
96
97 Ok(Bucket {
98 name: self.name.clone(),
99 http_client: Arc::clone(&self.http_client),
100 })
101 }
102}
103
104impl Bucket {
105 pub fn name(&self) -> &str {
107 &self.name
108 }
109
110 pub fn server_url(&self) -> &str {
112 &self.http_client.url()
113 }
114
115 pub async fn remove(&self) -> Result<()> {
121 let request = self
122 .http_client
123 .request(Method::DELETE, &format!("/b/{}", self.name));
124 self.http_client.send_request(request).await?;
125 Ok(())
126 }
127
128 pub async fn settings(&self) -> Result<BucketSettings> {
134 Ok(self.full_info().await?.settings)
135 }
136
137 pub async fn set_settings(&self, settings: BucketSettings) -> Result<()> {
147 self.http_client
148 .send_json::<BucketSettings>(Method::PUT, &format!("/b/{}", self.name), settings)
149 .await
150 }
151
152 pub async fn full_info(&self) -> Result<FullBucketInfo> {
154 self.http_client
155 .send_and_receive_json::<(), FullBucketInfo>(
156 Method::GET,
157 &format!("/b/{}", self.name),
158 None,
159 )
160 .await
161 }
162
163 pub async fn info(&self) -> Result<BucketInfo> {
165 Ok(self.full_info().await?.info)
166 }
167
168 pub async fn entries(&self) -> Result<Vec<EntryInfo>> {
170 Ok(self.full_info().await?.entries)
171 }
172
173 pub async fn rename_entry(&self, entry: &str, new_name: &str) -> Result<()> {
184 self.http_client
185 .send_json(
186 Method::PUT,
187 &format!("/b/{}/{}/rename", self.name, entry),
188 RenameEntry {
189 new_name: new_name.to_string(),
190 },
191 )
192 .await
193 }
194
195 pub async fn rename(&mut self, new_name: &str) -> Result<()> {
205 self.http_client
206 .send_json(
207 Method::PUT,
208 &format!("/b/{}/rename", self.name),
209 RenameEntry {
210 new_name: new_name.to_string(),
211 },
212 )
213 .await?;
214 self.name = new_name.to_string();
215 Ok(())
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use rstest::{fixture, rstest};
222
223 use reduct_base::error::ErrorCode;
224
225 use crate::client::tests::{bucket_settings, client};
226 use crate::client::ReductClient;
227
228 use super::*;
229
230 #[rstest]
231 #[tokio::test]
232 async fn test_bucket_full_info(#[future] bucket: Bucket) {
233 let bucket = bucket.await;
234 let FullBucketInfo {
235 info,
236 settings,
237 entries,
238 } = bucket.full_info().await.unwrap();
239 assert_eq!(info, bucket.info().await.unwrap());
240 assert_eq!(settings, bucket.settings().await.unwrap());
241 assert_eq!(entries, bucket.entries().await.unwrap());
242 }
243
244 #[rstest]
245 #[tokio::test]
246 async fn test_bucket_settings(#[future] bucket: Bucket, bucket_settings: BucketSettings) {
247 let bucket = bucket.await;
248 let settings = bucket.settings().await.unwrap();
249 assert_eq!(settings, bucket_settings);
250
251 let new_settings = BucketSettings {
252 quota_size: Some(100),
253 ..BucketSettings::default()
254 };
255
256 bucket.set_settings(new_settings.clone()).await.unwrap();
257 assert_eq!(
258 bucket.settings().await.unwrap(),
259 BucketSettings {
260 quota_size: new_settings.quota_size,
261 ..bucket_settings
262 }
263 );
264 }
265
266 #[rstest]
267 #[tokio::test]
268 async fn test_bucket_remove(#[future] bucket: Bucket) {
269 let bucket = bucket.await;
270 bucket.remove().await.unwrap();
271
272 assert_eq!(
273 bucket.info().await.err().unwrap().status,
274 ErrorCode::NotFound
275 );
276 }
277
278 #[rstest]
279 #[tokio::test]
280 async fn test_bucket_rename_entry(#[future] bucket: Bucket) {
281 let bucket = bucket.await;
282 bucket.rename_entry("entry-1", "new-entry-1").await.unwrap();
283 let entries = bucket.entries().await.unwrap();
284 assert!(entries.iter().any(|entry| entry.name == "new-entry-1"));
285 }
286
287 #[rstest]
288 #[tokio::test]
289 async fn test_bucket_rename(#[future] bucket: Bucket) {
290 let mut bucket = bucket.await;
291 bucket.rename("new-bucket").await.unwrap();
292 assert_eq!(bucket.name(), "new-bucket");
293 assert!(bucket.info().await.is_ok());
294 }
295
296 #[fixture]
297 pub async fn bucket(#[future] client: ReductClient) -> Bucket {
298 client.await.get_bucket("test-bucket-1").await.unwrap()
299 }
300}