Skip to main content

couchbase_core/mgmtx/
bucket_settings.rs

1/*
2 *
3 *  * Copyright (c) 2025 Couchbase, Inc.
4 *  *
5 *  * Licensed under the Apache License, Version 2.0 (the "License");
6 *  * you may not use this file except in compliance with the License.
7 *  * You may obtain a copy of the License at
8 *  *
9 *  *    http://www.apache.org/licenses/LICENSE-2.0
10 *  *
11 *  * Unless required by applicable law or agreed to in writing, software
12 *  * distributed under the License is distributed on an "AS IS" BASIS,
13 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  * See the License for the specific language governing permissions and
15 *  * limitations under the License.
16 *
17 */
18
19use crate::mgmtx::bucket_settings_json::BucketSettingsJson;
20use std::fmt::Display;
21use std::string::ToString;
22use std::time::Duration;
23use url::form_urlencoded::Serializer;
24
25#[derive(Default, Debug, Clone, PartialOrd, PartialEq, Eq)]
26pub struct BucketSettings {
27    pub flush_enabled: Option<bool>,
28    pub ram_quota_mb: Option<u64>,
29    pub replica_number: Option<u32>,
30    pub eviction_policy: Option<EvictionPolicyType>,
31    pub max_ttl: Option<Duration>,
32    pub compression_mode: Option<CompressionMode>,
33    pub durability_min_level: Option<DurabilityLevel>,
34    pub history_retention_collection_default: Option<bool>,
35    pub history_retention_bytes: Option<u64>,
36    pub history_retention_seconds: Option<u32>,
37    pub conflict_resolution_type: Option<ConflictResolutionType>,
38    pub replica_index: Option<bool>,
39    pub bucket_type: Option<BucketType>,
40    pub storage_backend: Option<StorageBackend>,
41    pub num_vbuckets: Option<u16>,
42}
43
44impl BucketSettings {
45    pub fn flush_enabled(mut self, flush_enabled: bool) -> Self {
46        self.flush_enabled = Some(flush_enabled);
47        self
48    }
49
50    pub fn ram_quota_mb(mut self, ram_quota_mb: u64) -> Self {
51        self.ram_quota_mb = Some(ram_quota_mb);
52        self
53    }
54
55    pub fn replica_number(mut self, replica_number: u32) -> Self {
56        self.replica_number = Some(replica_number);
57        self
58    }
59
60    pub fn eviction_policy(mut self, eviction_policy: impl Into<EvictionPolicyType>) -> Self {
61        self.eviction_policy = Some(eviction_policy.into());
62        self
63    }
64
65    pub fn max_ttl(mut self, max_ttl: Duration) -> Self {
66        self.max_ttl = Some(max_ttl);
67        self
68    }
69
70    pub fn compression_mode(mut self, compression_mode: impl Into<CompressionMode>) -> Self {
71        self.compression_mode = Some(compression_mode.into());
72        self
73    }
74
75    pub fn durability_min_level(
76        mut self,
77        durability_min_level: impl Into<DurabilityLevel>,
78    ) -> Self {
79        self.durability_min_level = Some(durability_min_level.into());
80        self
81    }
82
83    pub fn history_retention_collection_default(
84        mut self,
85        history_retention_collection_default: bool,
86    ) -> Self {
87        self.history_retention_collection_default = Some(history_retention_collection_default);
88        self
89    }
90
91    pub fn history_retention_bytes(mut self, history_retention_bytes: u64) -> Self {
92        self.history_retention_bytes = Some(history_retention_bytes);
93        self
94    }
95
96    pub fn history_retention_seconds(mut self, history_retention_seconds: u32) -> Self {
97        self.history_retention_seconds = Some(history_retention_seconds);
98        self
99    }
100
101    pub fn conflict_resolution_type(
102        mut self,
103        conflict_resolution_type: impl Into<ConflictResolutionType>,
104    ) -> Self {
105        self.conflict_resolution_type = Some(conflict_resolution_type.into());
106        self
107    }
108
109    pub fn replica_index(mut self, replica_index: bool) -> Self {
110        self.replica_index = Some(replica_index);
111        self
112    }
113
114    pub fn bucket_type(mut self, bucket_type: impl Into<BucketType>) -> Self {
115        self.bucket_type = Some(bucket_type.into());
116        self
117    }
118
119    pub fn storage_backend(mut self, storage_backend: impl Into<StorageBackend>) -> Self {
120        self.storage_backend = Some(storage_backend.into());
121        self
122    }
123
124    pub fn num_vbuckets(mut self, num_vbuckets: u16) -> Self {
125        self.num_vbuckets = Some(num_vbuckets);
126        self
127    }
128}
129
130#[derive(Debug, Clone, PartialOrd, PartialEq)]
131pub struct BucketDef {
132    pub name: String,
133    pub bucket_settings: BucketSettings,
134}
135
136impl BucketDef {
137    pub fn new(name: String, bucket_settings: BucketSettings) -> Self {
138        Self {
139            name,
140            bucket_settings,
141        }
142    }
143}
144
145impl From<BucketSettingsJson> for BucketDef {
146    fn from(settings: BucketSettingsJson) -> Self {
147        Self {
148            name: settings.name,
149            bucket_settings: BucketSettings {
150                flush_enabled: settings.controllers.as_ref().map(|c| {
151                    if let Some(f) = &c.flush {
152                        !f.is_empty()
153                    } else {
154                        false
155                    }
156                }),
157                ram_quota_mb: Some(settings.quota.raw_ram / 1024 / 1024),
158                replica_number: settings.replica_number,
159                eviction_policy: settings.eviction_policy,
160                max_ttl: settings.max_ttl.map(|d| Duration::from_secs(d as u64)),
161                compression_mode: settings.compression_mode,
162                durability_min_level: settings.durability_min_level,
163                history_retention_collection_default: settings.history_retention_collection_default,
164                history_retention_bytes: settings.history_retention_bytes,
165                history_retention_seconds: settings.history_retention_seconds,
166                conflict_resolution_type: settings.conflict_resolution_type,
167                replica_index: settings.replica_index,
168                bucket_type: settings.bucket_type,
169                storage_backend: settings.storage_backend,
170                num_vbuckets: settings.num_vbuckets,
171            },
172        }
173    }
174}
175
176#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
177pub struct BucketType(InnerBucketType);
178
179impl BucketType {
180    pub const COUCHBASE: BucketType = BucketType(InnerBucketType::Couchbase);
181
182    pub const EPHEMERAL: BucketType = BucketType(InnerBucketType::Ephemeral);
183
184    pub(crate) fn other(val: String) -> BucketType {
185        BucketType(InnerBucketType::Other(val))
186    }
187}
188
189impl Display for BucketType {
190    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191        match &self.0 {
192            InnerBucketType::Couchbase => write!(f, "membase"),
193            InnerBucketType::Ephemeral => write!(f, "ephemeral"),
194            InnerBucketType::Other(val) => write!(f, "unknown({val})"),
195        }
196    }
197}
198
199#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
200pub(crate) enum InnerBucketType {
201    Couchbase,
202    Ephemeral,
203    Other(String),
204}
205
206#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
207pub struct EvictionPolicyType(InnerEvictionPolicyType);
208
209impl EvictionPolicyType {
210    pub const VALUE_ONLY: EvictionPolicyType =
211        EvictionPolicyType(InnerEvictionPolicyType::ValueOnly);
212
213    pub const FULL: EvictionPolicyType = EvictionPolicyType(InnerEvictionPolicyType::Full);
214
215    pub const NOT_RECENTLY_USED: EvictionPolicyType =
216        EvictionPolicyType(InnerEvictionPolicyType::NotRecentlyUsed);
217
218    pub const NO_EVICTION: EvictionPolicyType =
219        EvictionPolicyType(InnerEvictionPolicyType::NoEviction);
220
221    pub(crate) fn other(val: String) -> EvictionPolicyType {
222        EvictionPolicyType(InnerEvictionPolicyType::Other(val))
223    }
224}
225
226impl Display for EvictionPolicyType {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        match &self.0 {
229            InnerEvictionPolicyType::ValueOnly => write!(f, "valueOnly"),
230            InnerEvictionPolicyType::Full => write!(f, "fullEviction"),
231            InnerEvictionPolicyType::NotRecentlyUsed => write!(f, "nruEviction"),
232            InnerEvictionPolicyType::NoEviction => write!(f, "noEviction"),
233            InnerEvictionPolicyType::Other(val) => write!(f, "unknown({val})"),
234        }
235    }
236}
237
238#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
239pub(crate) enum InnerEvictionPolicyType {
240    ValueOnly,
241    Full,
242    NotRecentlyUsed,
243    NoEviction,
244    Other(String),
245}
246
247#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
248pub struct CompressionMode(InnerCompressionMode);
249
250impl CompressionMode {
251    pub const OFF: CompressionMode = CompressionMode(InnerCompressionMode::Off);
252
253    pub const PASSIVE: CompressionMode = CompressionMode(InnerCompressionMode::Passive);
254
255    pub const ACTIVE: CompressionMode = CompressionMode(InnerCompressionMode::Active);
256
257    pub(crate) fn other(val: String) -> CompressionMode {
258        CompressionMode(InnerCompressionMode::Other(val))
259    }
260}
261
262impl Display for CompressionMode {
263    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264        match &self.0 {
265            InnerCompressionMode::Off => write!(f, "off"),
266            InnerCompressionMode::Passive => write!(f, "passive"),
267            InnerCompressionMode::Active => write!(f, "active"),
268            InnerCompressionMode::Other(val) => write!(f, "unknown({val})"),
269        }
270    }
271}
272
273#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
274pub(crate) enum InnerCompressionMode {
275    Off,
276    Passive,
277    Active,
278    Other(String),
279}
280
281#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
282pub struct DurabilityLevel(InnerDurabilityLevel);
283
284impl DurabilityLevel {
285    pub const NONE: DurabilityLevel = DurabilityLevel(InnerDurabilityLevel::None);
286
287    pub const MAJORITY: DurabilityLevel = DurabilityLevel(InnerDurabilityLevel::Majority);
288
289    pub const MAJORITY_AND_PERSIST_ACTIVE: DurabilityLevel =
290        DurabilityLevel(InnerDurabilityLevel::MajorityAndPersistActive);
291
292    pub const PERSIST_TO_MAJORITY: DurabilityLevel =
293        DurabilityLevel(InnerDurabilityLevel::PersistToMajority);
294
295    pub(crate) fn other(val: String) -> DurabilityLevel {
296        DurabilityLevel(InnerDurabilityLevel::Other(val))
297    }
298}
299
300impl Display for DurabilityLevel {
301    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302        match &self.0 {
303            InnerDurabilityLevel::None => write!(f, "none"),
304            InnerDurabilityLevel::Majority => write!(f, "majority"),
305            InnerDurabilityLevel::MajorityAndPersistActive => write!(f, "majorityAndPersistActive"),
306            InnerDurabilityLevel::PersistToMajority => write!(f, "persistToMajority"),
307            InnerDurabilityLevel::Other(val) => write!(f, "unknown({val})"),
308        }
309    }
310}
311
312#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
313pub(crate) enum InnerDurabilityLevel {
314    None,
315    Majority,
316    MajorityAndPersistActive,
317    PersistToMajority,
318    Other(String),
319}
320
321#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
322pub struct ConflictResolutionType(InnerConflictResolutionType);
323
324impl ConflictResolutionType {
325    pub const SEQUENCE_NUMBER: ConflictResolutionType =
326        ConflictResolutionType(InnerConflictResolutionType::SequenceNumber);
327
328    pub const TIMESTAMP: ConflictResolutionType =
329        ConflictResolutionType(InnerConflictResolutionType::Timestamp);
330
331    pub const CUSTOM: ConflictResolutionType =
332        ConflictResolutionType(InnerConflictResolutionType::Custom);
333
334    pub(crate) fn other(val: String) -> ConflictResolutionType {
335        ConflictResolutionType(InnerConflictResolutionType::Other(val))
336    }
337}
338
339impl Display for ConflictResolutionType {
340    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341        match &self.0 {
342            InnerConflictResolutionType::SequenceNumber => write!(f, "seqno"),
343            InnerConflictResolutionType::Timestamp => write!(f, "lww"),
344            InnerConflictResolutionType::Custom => write!(f, "custom"),
345            InnerConflictResolutionType::Other(val) => write!(f, "unknown({val})"),
346        }
347    }
348}
349
350#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
351pub(crate) enum InnerConflictResolutionType {
352    SequenceNumber,
353    Timestamp,
354    Custom,
355    Other(String),
356}
357
358#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
359pub struct StorageBackend(InnerStorageBackend);
360
361impl StorageBackend {
362    pub const COUCHSTORE: StorageBackend = StorageBackend(InnerStorageBackend::Couchstore);
363
364    pub const MAGMA: StorageBackend = StorageBackend(InnerStorageBackend::Magma);
365
366    pub(crate) fn other(val: String) -> StorageBackend {
367        StorageBackend(InnerStorageBackend::Other(val))
368    }
369}
370
371impl Display for StorageBackend {
372    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373        match &self.0 {
374            InnerStorageBackend::Couchstore => write!(f, "couchstore"),
375            InnerStorageBackend::Magma => write!(f, "magma"),
376            InnerStorageBackend::Other(val) => write!(f, "unknown({val})"),
377        }
378    }
379}
380
381#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
382pub(crate) enum InnerStorageBackend {
383    Couchstore,
384    Magma,
385    Other(String),
386}
387
388pub(crate) fn encode_bucket_settings(serializer: &mut Serializer<String>, opts: &BucketSettings) {
389    if let Some(flush) = opts.flush_enabled {
390        serializer.append_pair("flushEnabled", if flush { "1" } else { "0" });
391    }
392    if let Some(quota) = opts.ram_quota_mb {
393        serializer.append_pair("ramQuotaMB", quota.to_string().as_str());
394    }
395    if let Some(num) = opts.replica_number {
396        serializer.append_pair("replicaNumber", num.to_string().as_str());
397    }
398    if let Some(eviction_policy) = &opts.eviction_policy {
399        serializer.append_pair("evictionPolicy", eviction_policy.to_string().as_str());
400    }
401    if let Some(max_ttl) = &opts.max_ttl {
402        serializer.append_pair("maxTTL", max_ttl.as_secs().to_string().as_str());
403    }
404    if let Some(compression_mode) = &opts.compression_mode {
405        serializer.append_pair("compressionMode", compression_mode.to_string().as_str());
406    }
407    if let Some(durability_min_level) = &opts.durability_min_level {
408        serializer.append_pair(
409            "durabilityMinLevel",
410            durability_min_level.to_string().as_str(),
411        );
412    }
413    if let Some(retention) = opts.history_retention_bytes {
414        serializer.append_pair("historyRetentionBytes", retention.to_string().as_str());
415    }
416    if let Some(retention) = opts.history_retention_seconds {
417        serializer.append_pair("historyRetentionSeconds", retention.to_string().as_str());
418    }
419    if let Some(history_retention_collection_default) = &opts.history_retention_collection_default {
420        serializer.append_pair(
421            "historyRetentionCollectionDefault",
422            history_retention_collection_default.to_string().as_str(),
423        );
424    }
425    if let Some(conflict_resolution_type) = &opts.conflict_resolution_type {
426        serializer.append_pair(
427            "conflictResolutionType",
428            conflict_resolution_type.to_string().as_str(),
429        );
430    }
431    if let Some(index) = opts.replica_index {
432        serializer.append_pair("replicaIndex", if index { "1" } else { "0" });
433    }
434    if let Some(bucket_type) = &opts.bucket_type {
435        serializer.append_pair("bucketType", bucket_type.to_string().as_str());
436    }
437    if let Some(storage_backend) = &opts.storage_backend {
438        serializer.append_pair("storageBackend", storage_backend.to_string().as_str());
439    }
440    if let Some(num_vbuckets) = opts.num_vbuckets {
441        serializer.append_pair("numVBuckets", num_vbuckets.to_string().as_str());
442    }
443}