use std::{ env::temp_dir, path::{Path, PathBuf}, time::{SystemTime, UNIX_EPOCH}, }; use anyhow::Context; use tokio::io::AsyncWriteExt; pub struct StorageBackend { location: PathBuf, } impl StorageBackend { pub fn new(location: &Path) -> Self { Self { location: location.into(), } } pub fn temp() -> Self { Self::new(&temp_dir().join("nodata")) } pub async fn flush_segment(&self, topic: &str, buffer: &[u8]) -> anyhow::Result { let segment_key = uuid::Uuid::now_v7(); let segment_path = PathBuf::from("logs") .join(topic) .join(segment_key.to_string()); tracing::trace!("writing segment file: {}", segment_path.display()); let file_location = self.location.join(&segment_path); if let Some(parent) = file_location.parent() { tokio::fs::create_dir_all(parent) .await .context("failed to create storage backend dir")?; } let mut segment_file = tokio::fs::File::create(&file_location).await?; segment_file.write_all(buffer).await?; segment_file.flush().await?; Ok(segment_key.to_string()) } pub async fn append_index( &self, topic: &str, segment_file: &str, time: SystemTime, ) -> anyhow::Result<()> { let index_path = PathBuf::from("indexes").join(topic); tracing::trace!("writing index file: {}", index_path.display()); let file_location = self.location.join(&index_path); if let Some(parent) = file_location.parent() { tokio::fs::create_dir_all(parent) .await .context("failed to create storage backend dir, index")?; } if !file_location.exists() { tokio::fs::File::create(&file_location).await?; } let mut index_file = tokio::fs::File::options() .append(true) .open(&file_location) .await?; index_file .write_all( format!( "{},{}\n", time.duration_since(UNIX_EPOCH) .expect("to be able to get time") .as_secs(), segment_file ) .as_bytes(), ) .await?; index_file.flush().await?; Ok(()) } }