From 1e9c97c21f8a4e55420712b054895ff8b4f9a849 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Fri, 19 Jun 2026 01:40:38 +0800 Subject: feat(rola-bucket): add bucket bind management Implement bucket bind CRUD operations and config loading, along with CLI integration for listing, setting, and removing bucket bindings. --- rola-bucket/src/bucket/bind.rs | 210 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 rola-bucket/src/bucket/bind.rs (limited to 'rola-bucket/src/bucket/bind.rs') diff --git a/rola-bucket/src/bucket/bind.rs b/rola-bucket/src/bucket/bind.rs new file mode 100644 index 0000000..87f3382 --- /dev/null +++ b/rola-bucket/src/bucket/bind.rs @@ -0,0 +1,210 @@ +use serde::{Deserialize, Serialize}; +use shared_constants::bucket::PREFIX_BUCKET_BIND; +use space_system::{Space, SpaceError}; +use std::borrow::Borrow; +use std::cmp::Ordering; +use std::fmt; +use std::fs::ReadDir; +use std::io::ErrorKind::NotFound; +use std::ops::{Deref, DerefMut}; + +use crate::{AsyncBucketTransferProtocol, Bucket}; + +#[cfg(test)] +mod test; + +/// Represents a binding between a bucket and a URL. +/// +/// `BucketBind` is a newtype wrapper around a `String` that stores a URL +/// associated with a bucket. It provides convenient access to the underlying +/// URL string through `Deref`, `DerefMut`, `Borrow`, and `Display` trait +/// implementations. +#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, Clone)] +pub struct BucketBind { + /// The index of the bucket bind + index: u8, + + /// The URL associated with the bucket bind. + url: String, +} + +impl BucketBind { + /// Creates a new `BucketBind` with the given URL. + fn new(index: u8, url: impl Into) -> Self { + Self { + index, + url: url.into(), + } + } + + /// Returns the index of the bucket bind. + pub fn get_index(&self) -> u8 { + self.index + } + + /// Returns a reference to the URL of the bucket bind. + pub fn get_url(&self) -> &str { + &self.url + } +} + +/// Reads all bucket bind records from the space. +/// +/// This function traverses the root directory of the specified space, filters files +/// that start with a specific prefix (`PREFIX_BUCKET_BIND`), parses the index and URL +/// from each binding record, and returns them as `BucketBind` objects. +pub fn read_bucket_binds( + space: &Space>, +) -> Result, SpaceError> { + // Fixed prefix for bucket bind filenames + const PREFIX: &str = PREFIX_BUCKET_BIND; + + // Open a read stream for the space root directory + let reader: ReadDir = space.read_dir(".")?; + let mut binds = Vec::new(); + + // Loop through each entry in the directory + for entry in reader { + let entry = entry?; + let file_name = entry.file_name(); + let name = file_name.to_string_lossy().to_string(); + + // Only process files starting with the bind prefix + if let Some(suffix) = name.strip_prefix(PREFIX) { + // Extract the part after the prefix as the index string + // Attempt to parse the suffix as a u8 index value + if let Ok(index) = suffix.parse::() { + // Read the file content as the URL + let content = space.read_to_string(&name)?; + let url = content.trim().to_string(); + + // Add the parsed binding record to the list + binds.push(BucketBind::new(index, url)); + } + } + } + + // Sort by index before returning + binds.sort(); + Ok(binds) +} + +/// Writes a bucket bind record to the space. +/// +/// This function creates or updates a binding between a bucket and a URL +/// at the specified index. It writes the URL content to a file named +/// with the prefix `PREFIX_BUCKET_BIND` followed by the zero-padded index. +pub fn write_bucket_bind( + space: &Space>, + idx: u8, + url: &str, +) -> Result<(), SpaceError> { + const PREFIX: &str = PREFIX_BUCKET_BIND; + let file_name = format!("{}{:03}", PREFIX, idx); + space.write(&file_name, url.trim()) +} + +/// Reads a single bucket bind record from the space by index. +/// +/// This function looks for a file named with the prefix `PREFIX_BUCKET_BIND` +/// followed by the zero-padded index, reads its content as a URL, and returns +/// the corresponding `BucketBind`. Returns `None` if the file does not exist. +pub fn read_bucket_bind( + space: &Space>, + idx: u8, +) -> Result, SpaceError> { + const PREFIX: &str = PREFIX_BUCKET_BIND; + let file_name = format!("{}{:03}", PREFIX, idx); + + match space.read_to_string(&file_name) { + Ok(content) => { + let url = content.trim().to_string(); + Ok(Some(BucketBind::new(idx, url))) + } + Err(SpaceError::Io(err)) => { + if err.kind() == NotFound { + Ok(None) + } else { + Err(SpaceError::Io(err)) + } + } + Err(e) => Err(e), + } +} + +/// Checks whether a bucket bind record exists at the given index. +/// +/// Returns `true` if a file named with the prefix `PREFIX_BUCKET_BIND` followed +/// by the zero-padded index exists in the space, `false` otherwise. +pub fn check_bucket_bind_exists( + space: &Space>, + idx: u8, +) -> Result { + const PREFIX: &str = PREFIX_BUCKET_BIND; + let file_name = format!("{}{:03}", PREFIX, idx); + + match space.read_to_string(&file_name) { + Ok(_) => Ok(true), + Err(SpaceError::Io(err)) => { + if err.kind() == NotFound { + Ok(false) + } else { + Err(SpaceError::Io(err)) + } + } + Err(e) => Err(e), + } +} + +/// Removes a bucket bind record from the space by index. +/// +/// This function deletes the file named with the prefix `PREFIX_BUCKET_BIND` +/// followed by the zero-padded index from the space. Returns `Ok(())` if the +/// deletion succeeds, or an error if the operation fails (including if the +/// file does not exist). +pub fn remove_bucket_bind( + space: &Space>, + idx: u8, +) -> Result<(), SpaceError> { + const PREFIX: &str = PREFIX_BUCKET_BIND; + let file_name = format!("{}{:03}", PREFIX, idx); + space.remove_file(&file_name) +} + +impl Ord for BucketBind { + fn cmp(&self, other: &Self) -> Ordering { + self.index.cmp(&other.index) + } +} + +impl PartialOrd for BucketBind { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Deref for BucketBind { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.url + } +} + +impl DerefMut for BucketBind { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.url + } +} + +impl Borrow for BucketBind { + fn borrow(&self) -> &String { + &self.url + } +} + +impl fmt::Display for BucketBind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.url) + } +} -- cgit