diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-19 23:42:19 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-19 23:42:19 +0800 |
| commit | 40183e431da97fe0377b2f2e2ea47b3a14376680 (patch) | |
| tree | 0c117e2ec707f3ad33206f1e320af2b439f80dbe /rola-bucket | |
| parent | d36acf196f7475a91550cc64c9c9a545adb65c50 (diff) | |
feat(rola-bucket): add TOML bucket config read/write
Add functions to read and write BucketConfig from and to TOML files,
including space-aware variants. Replace hardcoded resource file with
serializable config struct.
Diffstat (limited to 'rola-bucket')
| -rw-r--r-- | rola-bucket/Cargo.toml | 1 | ||||
| -rw-r--r-- | rola-bucket/res/bucket.toml | 4 | ||||
| -rw-r--r-- | rola-bucket/src/bucket/config.rs | 83 | ||||
| -rw-r--r-- | rola-bucket/src/bucket/config/test.rs | 116 | ||||
| -rw-r--r-- | rola-bucket/src/bucket/init.rs | 8 | ||||
| -rw-r--r-- | rola-bucket/src/bucket/space.rs | 5 |
6 files changed, 200 insertions, 17 deletions
diff --git a/rola-bucket/Cargo.toml b/rola-bucket/Cargo.toml index 8899591..e5694dc 100644 --- a/rola-bucket/Cargo.toml +++ b/rola-bucket/Cargo.toml @@ -16,3 +16,4 @@ thiserror.workspace = true tokio.workspace = true serde.workspace = true log.workspace = true +toml.workspace = true diff --git a/rola-bucket/res/bucket.toml b/rola-bucket/res/bucket.toml deleted file mode 100644 index adc7770..0000000 --- a/rola-bucket/res/bucket.toml +++ /dev/null @@ -1,4 +0,0 @@ -[bucket] -# Bucket 类型 - type = "client" # 本地存储,使用本地 ID -# type = "bucket" # 中心存储,使用全局 ID diff --git a/rola-bucket/src/bucket/config.rs b/rola-bucket/src/bucket/config.rs index 559db15..2fd111c 100644 --- a/rola-bucket/src/bucket/config.rs +++ b/rola-bucket/src/bucket/config.rs @@ -1,9 +1,15 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use space_system::{Space, SpaceError}; + +use crate::{AsyncBucketTransferProtocol, Bucket}; + +#[cfg(test)] +mod test; /// Configuration for a bucket. /// /// This struct defines how a bucket should be configured, including its type. -#[derive(Default, Deserialize)] +#[derive(Default, Serialize, Deserialize)] pub struct BucketConfig { /// The type of the bucket, e.g., client bucket or normal bucket. /// @@ -15,7 +21,7 @@ pub struct BucketConfig { /// Enum for bucket types, used to distinguish different types of buckets. /// /// When deserializing, field names are mapped to string values in TOML via `serde(rename)`. -#[derive(Default, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] pub enum BucketType { /// Client bucket /// Uses local ID, mapped to remote ID via IDMAP @@ -28,3 +34,74 @@ pub enum BucketType { #[serde(rename = "bucket")] Bucket, } + +/// Reads the bucket configuration from a file path. +/// +/// This function reads the bucket configuration file at the given `path`. +/// If the file cannot be read or the content cannot be parsed as TOML, +/// a default `BucketConfig` will be used instead of returning an error. +/// +/// # Note +/// +/// - If the configuration file cannot be read (e.g., file not found, permission denied), +/// a default [`BucketConfig`] is returned. +/// - If the file content is not valid TOML or does not match the expected structure, +/// a default [`BucketConfig`] is returned as well. +pub fn read_bucket_config_from_path( + path: impl AsRef<std::path::Path>, +) -> Result<BucketConfig, SpaceError> { + let config_content = match std::fs::read_to_string(path) { + Ok(content) => content, + Err(_) => return Ok(BucketConfig::default()), + }; + Ok(toml::from_str(&config_content).unwrap_or_default()) +} + +/// Writes the bucket configuration to a file path. +/// +/// This function serializes the given bucket configuration as TOML and +/// writes it to the specified `path`. If the serialization or write fails, +/// the error is returned as a [`SpaceError`]. +pub fn write_bucket_config_to_path( + path: impl AsRef<std::path::Path>, + config: &BucketConfig, +) -> Result<(), SpaceError> { + let config_content = toml::to_string(config).map_err(|e| { + SpaceError::Other(format!("Failed to serialize bucket config to TOML: {}", e)) + })?; + std::fs::write(path, config_content.as_bytes()) + .map_err(|e| SpaceError::Other(format!("Failed to write bucket config to file: {}", e)))?; + Ok(()) +} + +/// Reads the bucket configuration from the space, with shared with_path logic. +/// +/// This function attempts to read the bucket configuration file (`FILE_BUCKET_ROOT_CONFIG`) +/// from the given `space`. If the file cannot be read or the content cannot be parsed as TOML, +/// a default `BucketConfig` will be used instead of returning an error. +/// +/// # Note +/// +/// - If the configuration file cannot be read (e.g., file not found, permission denied), +/// a default [`BucketConfig`] is returned. +/// - If the file content is not valid TOML or does not match the expected structure, +/// a default [`BucketConfig`] is returned as well. +pub fn read_bucket_config<Protocol: AsyncBucketTransferProtocol + Send + Sync>( + space: &Space<Bucket<Protocol>>, +) -> Result<BucketConfig, SpaceError> { + let path = space.local_path(shared_constants::common::FILE_BUCKET_ROOT_CONFIG)?; + read_bucket_config_from_path(path) +} + +/// Writes the bucket configuration to the space, with shared with_path logic. +/// +/// This function attempts to write the bucket configuration file (`FILE_BUCKET_ROOT_CONFIG`) +/// to the given `space` as a TOML string. If the serialization or write fails, the error +/// is returned. +pub fn write_bucket_config<Protocol: AsyncBucketTransferProtocol + Send + Sync>( + space: &Space<Bucket<Protocol>>, + config: &BucketConfig, +) -> Result<(), SpaceError> { + let path = space.local_path(shared_constants::common::FILE_BUCKET_ROOT_CONFIG)?; + write_bucket_config_to_path(path, config) +} diff --git a/rola-bucket/src/bucket/config/test.rs b/rola-bucket/src/bucket/config/test.rs new file mode 100644 index 0000000..d586045 --- /dev/null +++ b/rola-bucket/src/bucket/config/test.rs @@ -0,0 +1,116 @@ +use shared_functions::rola_test_sandbox; + +use crate::config::{ + BucketConfig, BucketType, read_bucket_config_from_path, write_bucket_config_to_path, +}; + +#[test] +fn test_read_bucket_config_from_path_file_not_found_returns_default() { + let sandbox = rola_test_sandbox("bucket_config_read_file_not_found"); + let path = sandbox.path.join("nonexistent.toml"); + + let config = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(config.bucket_type, BucketType::Bucket); +} + +#[test] +fn test_read_bucket_config_from_path_invalid_toml_returns_default() { + let sandbox = rola_test_sandbox("bucket_config_read_invalid_toml"); + let path = sandbox.path.join("config.toml"); + std::fs::write(&path, "this is not valid TOML {{{").unwrap(); + + let config = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(config.bucket_type, BucketType::Bucket); +} + +#[test] +fn test_read_bucket_config_from_path_valid_client_bucket() { + let sandbox = rola_test_sandbox("bucket_config_read_valid_client"); + let path = sandbox.path.join("config.toml"); + std::fs::write(&path, r#"type = "client""#).unwrap(); + + let config = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(config.bucket_type, BucketType::ClientBucket); +} + +#[test] +fn test_read_bucket_config_from_path_valid_normal_bucket() { + let sandbox = rola_test_sandbox("bucket_config_read_valid_normal"); + let path = sandbox.path.join("config.toml"); + std::fs::write(&path, r#"type = "bucket""#).unwrap(); + + let config = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(config.bucket_type, BucketType::Bucket); +} + +#[test] +fn test_read_bucket_config_from_path_empty_file_returns_default() { + let sandbox = rola_test_sandbox("bucket_config_read_empty"); + let path = sandbox.path.join("config.toml"); + std::fs::write(&path, "").unwrap(); + + let config = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(config.bucket_type, BucketType::Bucket); +} + +#[test] +fn test_read_bucket_config_from_path_unknown_type_returns_default() { + let sandbox = rola_test_sandbox("bucket_config_read_unknown_type"); + let path = sandbox.path.join("config.toml"); + std::fs::write(&path, r#"type = "something_else""#).unwrap(); + + let config = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(config.bucket_type, BucketType::Bucket); +} + +#[test] +fn test_write_and_read_roundtrip_client_bucket() { + let sandbox = rola_test_sandbox("bucket_config_roundtrip_client"); + let path = sandbox.path.join("config.toml"); + + let config = BucketConfig { + bucket_type: BucketType::ClientBucket, + }; + write_bucket_config_to_path(&path, &config).unwrap(); + + let loaded = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(loaded.bucket_type, BucketType::ClientBucket); +} + +#[test] +fn test_write_and_read_roundtrip_normal_bucket() { + let sandbox = rola_test_sandbox("bucket_config_roundtrip_normal"); + let path = sandbox.path.join("config.toml"); + + let config = BucketConfig { + bucket_type: BucketType::Bucket, + }; + write_bucket_config_to_path(&path, &config).unwrap(); + + let loaded = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(loaded.bucket_type, BucketType::Bucket); +} + +#[test] +fn test_write_bucket_config_to_path_invalid_dir_fails() { + let path = "/nonexistent_directory_12345/config.toml"; + + let config = BucketConfig::default(); + let result = write_bucket_config_to_path(&path, &config); + assert!(result.is_err()); +} + +#[test] +fn test_write_bucket_config_overwrites_existing_file() { + let sandbox = rola_test_sandbox("bucket_config_overwrite"); + let path = sandbox.path.join("config.toml"); + std::fs::write(&path, r#"type = "client""#).unwrap(); + + let config = BucketConfig { + bucket_type: BucketType::Bucket, + }; + write_bucket_config_to_path(&path, &config).unwrap(); + + let loaded = read_bucket_config_from_path(&path).unwrap(); + assert_eq!(loaded.bucket_type, BucketType::Bucket); +} diff --git a/rola-bucket/src/bucket/init.rs b/rola-bucket/src/bucket/init.rs index 6834009..d2dda24 100644 --- a/rola-bucket/src/bucket/init.rs +++ b/rola-bucket/src/bucket/init.rs @@ -12,6 +12,8 @@ use shared_constants::{ }; use space_system::SpaceError; +use crate::config::{BucketConfig, write_bucket_config_to_path}; + pub(crate) fn init_bucket_at(path: PathBuf) -> Result<(), SpaceError> { let bucket_config_file = path.join(FILE_BUCKET_ROOT_CONFIG); @@ -21,16 +23,12 @@ pub(crate) fn init_bucket_at(path: PathBuf) -> Result<(), SpaceError> { return Err(SpaceError::RequireEmptyDirectory); } - write_config(&bucket_config_file)?; + write_bucket_config_to_path(&bucket_config_file, &BucketConfig::default())?; create_dirs(&path)?; Ok(()) } -fn write_config(bucket_config_file: &Path) -> Result<(), SpaceError> { - fs::write(bucket_config_file, include_str!("../../res/bucket.toml")).map_err(SpaceError::Io) -} - fn create_dirs(bucket_dir: &Path) -> Result<(), SpaceError> { let dirs = [ DIR_BUCKET_OBJ, diff --git a/rola-bucket/src/bucket/space.rs b/rola-bucket/src/bucket/space.rs index 353075d..1775914 100644 --- a/rola-bucket/src/bucket/space.rs +++ b/rola-bucket/src/bucket/space.rs @@ -1,6 +1,4 @@ -// use log::trace; use shared_constants::common::FILE_BUCKET_ROOT_CONFIG; -use shared_functions::trace; use space_system::{SpaceRoot, SpaceRootFindPattern}; use crate::{AsyncBucketTransferProtocol, Bucket, bucket::init::init_bucket_at}; @@ -11,10 +9,7 @@ impl<Protocol: AsyncBucketTransferProtocol + Send + Sync> SpaceRoot for Bucket<P } fn create_space(path: &std::path::Path) -> Result<(), space_system::SpaceError> { - let path_str = path.display().to_string(); - trace!("Creating bucket at: {}", &path_str); init_bucket_at(path.into())?; - trace!("Bucket created at: {}", &path_str); Ok(()) } } |
