use std::path::PathBuf;
#[derive(Debug, Clone, Default)]
pub enum StorageMode {
#[default]
InMemory,
Vortex {
url: String,
},
}
#[derive(Debug, Clone)]
pub enum VortexLocation {
Local { base_path: PathBuf },
#[cfg(feature = "cloud-storage")]
S3 { url: String },
}
impl StorageMode {
pub fn is_cloud(&self) -> bool {
match self {
Self::InMemory => false,
Self::Vortex { url } => {
let _ = url; #[cfg(feature = "cloud-storage")]
{
url.starts_with("s3://")
}
#[cfg(not(feature = "cloud-storage"))]
{
false
}
}
}
}
pub fn classify(&self) -> Result<Option<VortexLocation>, String> {
match self {
Self::InMemory => Ok(None),
Self::Vortex { url } => {
if url.starts_with("s3://") {
#[cfg(feature = "cloud-storage")]
{
return Ok(Some(VortexLocation::S3 { url: url.clone() }));
}
#[cfg(not(feature = "cloud-storage"))]
{
return Err(format!(
"S3 URL '{url}' requires the 'cloud-storage' feature"
));
}
}
if let Some(colon) = url.find("://") {
let scheme = &url[..colon];
if scheme != "file" {
return Err(format!(
"unsupported storage URL scheme '{scheme}://' in '{url}'. \
Use a local path, 'file://', or (with cloud-storage feature) 's3://'"
));
}
}
let path = if let Some(rest) = url.strip_prefix("file://") {
PathBuf::from(rest)
} else {
PathBuf::from(url)
};
Ok(Some(VortexLocation::Local { base_path: path }))
}
}
}
pub fn local_base_path(&self) -> Option<PathBuf> {
match self.classify() {
Ok(Some(VortexLocation::Local { base_path })) => Some(base_path),
_ => None,
}
}
#[cfg(feature = "cloud-storage")]
pub fn cloud_base_url(&self) -> Option<&str> {
if let Self::Vortex { url } = self {
if url.starts_with("s3://") {
return Some(url.as_str());
}
}
None
}
pub fn file_extension(&self) -> &'static str {
match self {
Self::InMemory => "",
Self::Vortex { .. } => "vortex",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inmemory_is_not_cloud() {
assert!(!StorageMode::InMemory.is_cloud());
}
#[test]
fn inmemory_classify_is_none() {
assert!(StorageMode::InMemory.classify().unwrap().is_none());
}
#[test]
fn vortex_local_abs_path() {
let mode = StorageMode::Vortex {
url: "/tmp/rhei-data".to_string(),
};
assert!(!mode.is_cloud());
assert_eq!(mode.file_extension(), "vortex");
match mode.classify().unwrap().unwrap() {
VortexLocation::Local { base_path } => {
assert_eq!(base_path, PathBuf::from("/tmp/rhei-data"));
}
#[cfg(feature = "cloud-storage")]
VortexLocation::S3 { .. } => panic!("expected Local"),
}
}
#[test]
fn vortex_local_rel_path() {
let mode = StorageMode::Vortex {
url: "./data/olap".to_string(),
};
match mode.classify().unwrap().unwrap() {
VortexLocation::Local { base_path } => {
assert_eq!(base_path, PathBuf::from("./data/olap"));
}
#[cfg(feature = "cloud-storage")]
VortexLocation::S3 { .. } => panic!("expected Local"),
}
}
#[test]
fn vortex_file_url() {
let mode = StorageMode::Vortex {
url: "file:///tmp/rhei-vortex".to_string(),
};
match mode.classify().unwrap().unwrap() {
VortexLocation::Local { base_path } => {
assert_eq!(base_path, PathBuf::from("/tmp/rhei-vortex"));
}
#[cfg(feature = "cloud-storage")]
VortexLocation::S3 { .. } => panic!("expected Local"),
}
}
#[test]
fn vortex_unsupported_scheme_rejects() {
for url in [
"gs://my-bucket/prefix",
"http://example.com/data",
"https://example.com/data",
"az://account/container",
"azure://account/container",
"ftp://server/path",
] {
let mode = StorageMode::Vortex { url: url.into() };
let err = mode
.classify()
.expect_err(&format!("expected reject for '{url}', got Ok"));
assert!(
err.contains("unsupported storage URL scheme"),
"expected 'unsupported storage URL scheme' in error for '{url}', got: {err}"
);
}
}
#[cfg(feature = "cloud-storage")]
#[test]
fn vortex_s3_url() {
let mode = StorageMode::Vortex {
url: "s3://my-bucket/rhei-data".to_string(),
};
assert!(mode.is_cloud());
assert_eq!(mode.file_extension(), "vortex");
assert_eq!(mode.cloud_base_url(), Some("s3://my-bucket/rhei-data"));
match mode.classify().unwrap().unwrap() {
VortexLocation::S3 { url } => assert_eq!(url, "s3://my-bucket/rhei-data"),
VortexLocation::Local { .. } => panic!("expected S3"),
}
}
#[test]
fn vortex_file_extension() {
assert_eq!(StorageMode::InMemory.file_extension(), "");
assert_eq!(
StorageMode::Vortex {
url: "/tmp/test".to_string()
}
.file_extension(),
"vortex"
);
}
}