use crate::{
bucket::DEFAULT_BUCKET_NAME,
error::{Error, Result},
types::{KeyRange, Value},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BatchOperation {
Put {
bucket: String,
key: Vec<u8>,
value: Value,
},
Delete {
bucket: String,
key: Vec<u8>,
},
DeleteRange {
bucket: String,
range: KeyRange,
},
}
impl BatchOperation {
#[must_use]
pub fn bucket(&self) -> &str {
match self {
Self::Put { bucket, .. }
| Self::Delete { bucket, .. }
| Self::DeleteRange { bucket, .. } => bucket,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct WriteBatch {
operations: Vec<BatchOperation>,
}
impl WriteBatch {
#[must_use]
pub const fn new() -> Self {
Self {
operations: Vec::new(),
}
}
pub fn put(&mut self, key: impl Into<Vec<u8>>, value: impl Into<Value>) {
self.operations.push(BatchOperation::Put {
bucket: DEFAULT_BUCKET_NAME.to_owned(),
key: key.into(),
value: value.into(),
});
}
pub fn put_bucket(
&mut self,
bucket: impl Into<String>,
key: impl Into<Vec<u8>>,
value: impl Into<Value>,
) -> Result<()> {
let bucket = bucket.into();
validate_named_bucket(&bucket)?;
self.operations.push(BatchOperation::Put {
bucket,
key: key.into(),
value: value.into(),
});
Ok(())
}
pub fn delete(&mut self, key: impl Into<Vec<u8>>) {
self.operations.push(BatchOperation::Delete {
bucket: DEFAULT_BUCKET_NAME.to_owned(),
key: key.into(),
});
}
pub fn delete_bucket(
&mut self,
bucket: impl Into<String>,
key: impl Into<Vec<u8>>,
) -> Result<()> {
let bucket = bucket.into();
validate_named_bucket(&bucket)?;
self.operations.push(BatchOperation::Delete {
bucket,
key: key.into(),
});
Ok(())
}
pub fn delete_range(&mut self, range: KeyRange) {
self.operations.push(BatchOperation::DeleteRange {
bucket: DEFAULT_BUCKET_NAME.to_owned(),
range,
});
}
pub fn delete_range_bucket(
&mut self,
bucket: impl Into<String>,
range: KeyRange,
) -> Result<()> {
let bucket = bucket.into();
validate_named_bucket(&bucket)?;
self.operations
.push(BatchOperation::DeleteRange { bucket, range });
Ok(())
}
#[must_use]
pub fn operations(&self) -> &[BatchOperation] {
&self.operations
}
#[must_use]
pub fn into_operations(self) -> Vec<BatchOperation> {
self.operations
}
#[must_use]
pub fn len(&self) -> usize {
self.operations.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.operations.is_empty()
}
}
fn validate_named_bucket(bucket: &str) -> Result<()> {
if bucket.is_empty() {
return Err(Error::invalid_options("bucket name cannot be empty"));
}
if bucket == DEFAULT_BUCKET_NAME {
return Err(Error::invalid_options(
"default bucket writes use default batch methods",
));
}
Ok(())
}