photoncloud-monorepo/lightningstor/crates/lightningstor-storage/src/backend.rs
centra a7ec7e2158 Add T026 practical test + k8shost to flake + workspace files
- Created T026-practical-test task.yaml for MVP smoke testing
- Added k8shost-server to flake.nix (packages, apps, overlays)
- Staged all workspace directories for nix flake build
- Updated flake.nix shellHook to include k8shost

Resolves: T026.S1 blocker (R8 - nix submodule visibility)
2025-12-09 06:07:50 +09:00

159 lines
4.8 KiB
Rust

//! Storage backend trait definition
use async_trait::async_trait;
use bytes::Bytes;
use lightningstor_types::ObjectId;
use std::io;
use thiserror::Error;
/// Storage backend error
#[derive(Debug, Error)]
pub enum StorageError {
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("Object not found: {0}")]
NotFound(ObjectId),
#[error("Storage backend error: {0}")]
Backend(String),
#[error("Invalid object ID: {0}")]
InvalidObjectId(String),
}
/// Storage result type
pub type StorageResult<T> = std::result::Result<T, StorageError>;
impl From<StorageError> for lightningstor_types::Error {
fn from(err: StorageError) -> Self {
match err {
StorageError::Io(e) => lightningstor_types::Error::StorageError(e.to_string()),
StorageError::NotFound(id) => {
lightningstor_types::Error::ObjectNotFound {
bucket: String::new(),
key: id.to_string(),
}
}
StorageError::Backend(msg) => lightningstor_types::Error::StorageError(msg),
StorageError::InvalidObjectId(msg) => {
lightningstor_types::Error::InvalidArgument(msg)
}
}
}
}
/// Storage backend trait for object data storage
///
/// This trait abstracts the storage of object data, allowing different
/// implementations (local filesystem, distributed storage, cloud storage).
#[async_trait]
pub trait StorageBackend: Send + Sync {
/// Write object data
///
/// # Arguments
/// * `object_id` - Internal object identifier
/// * `data` - Object data bytes
///
/// # Returns
/// * `Ok(())` if write succeeded
/// * `Err(StorageError)` if write failed
async fn put_object(&self, object_id: &ObjectId, data: Bytes) -> StorageResult<()>;
/// Read object data
///
/// # Arguments
/// * `object_id` - Internal object identifier
///
/// # Returns
/// * `Ok(Bytes)` if object exists
/// * `Err(StorageError::NotFound)` if object does not exist
/// * `Err(StorageError)` for other errors
async fn get_object(&self, object_id: &ObjectId) -> StorageResult<Bytes>;
/// Delete object data
///
/// # Arguments
/// * `object_id` - Internal object identifier
///
/// # Returns
/// * `Ok(())` if delete succeeded (or object didn't exist)
/// * `Err(StorageError)` for other errors
async fn delete_object(&self, object_id: &ObjectId) -> StorageResult<()>;
/// Check if object exists
///
/// # Arguments
/// * `object_id` - Internal object identifier
///
/// # Returns
/// * `Ok(true)` if object exists
/// * `Ok(false)` if object does not exist
/// * `Err(StorageError)` for other errors
async fn object_exists(&self, object_id: &ObjectId) -> StorageResult<bool>;
/// Get object size in bytes
///
/// # Arguments
/// * `object_id` - Internal object identifier
///
/// # Returns
/// * `Ok(u64)` if object exists
/// * `Err(StorageError::NotFound)` if object does not exist
/// * `Err(StorageError)` for other errors
async fn object_size(&self, object_id: &ObjectId) -> StorageResult<u64>;
/// Write part data (for multipart uploads)
///
/// # Arguments
/// * `upload_id` - Multipart upload ID
/// * `part_number` - Part number (1-based)
/// * `data` - Part data bytes
///
/// # Returns
/// * `Ok(())` if write succeeded
/// * `Err(StorageError)` if write failed
async fn put_part(
&self,
upload_id: &str,
part_number: u32,
data: Bytes,
) -> StorageResult<()>;
/// Read part data (for multipart uploads)
///
/// # Arguments
/// * `upload_id` - Multipart upload ID
/// * `part_number` - Part number (1-based)
///
/// # Returns
/// * `Ok(Bytes)` if part exists
/// * `Err(StorageError::NotFound)` if part does not exist
/// * `Err(StorageError)` for other errors
async fn get_part(
&self,
upload_id: &str,
part_number: u32,
) -> StorageResult<Bytes>;
/// Delete part data (for multipart uploads)
///
/// # Arguments
/// * `upload_id` - Multipart upload ID
/// * `part_number` - Part number (1-based)
///
/// # Returns
/// * `Ok(())` if delete succeeded
/// * `Err(StorageError)` for other errors
async fn delete_part(&self, upload_id: &str, part_number: u32) -> StorageResult<()>;
/// Delete all parts for a multipart upload
///
/// # Arguments
/// * `upload_id` - Multipart upload ID
///
/// # Returns
/// * `Ok(())` if delete succeeded
/// * `Err(StorageError)` for other errors
async fn delete_upload_parts(&self, upload_id: &str) -> StorageResult<()>;
}