use serde::{Deserialize, Serialize};
use crate::StorageUri;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
pub uri: StorageUri,
#[serde(default)]
pub prefix: Option<String>,
#[serde(default)]
pub public: bool,
#[serde(default)]
pub endpoint: Option<String>,
#[serde(default)]
pub region: Option<String>,
}
impl StorageConfig {
#[must_use]
pub fn resolve_key(&self, key: &str) -> String {
let key = key.trim_start_matches('/');
match self
.prefix
.as_deref()
.map(|prefix| prefix.trim_matches('/'))
.filter(|prefix| !prefix.is_empty())
{
Some(prefix) if key.is_empty() => prefix.to_string(),
Some(prefix) => format!("{prefix}/{key}"),
None => key.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_local_config() {
let toml_str = r#"
uri = "./data/parquet"
prefix = "datasets/"
"#;
let config: StorageConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.resolve_key("file.parquet"), "datasets/file.parquet");
}
#[test]
fn parse_s3_config() {
let toml_str = r#"
uri = "s3://my-bucket"
prefix = "v1/"
endpoint = "http://localhost:9000"
region = "us-east-1"
"#;
let config: StorageConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.uri.scheme(), "s3");
assert_eq!(config.endpoint.as_deref(), Some("http://localhost:9000"));
}
#[test]
fn no_prefix() {
let toml_str = r#"uri = "gs://my-bucket""#;
let config: StorageConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.resolve_key("file.parquet"), "file.parquet");
}
#[test]
fn resolve_key_normalizes_slashes() {
let config = StorageConfig {
uri: StorageUri::Local("./data".into()),
prefix: Some("/datasets/".to_string()),
public: false,
endpoint: None,
region: None,
};
assert_eq!(
config.resolve_key("/nested/file.parquet"),
"datasets/nested/file.parquet"
);
}
}