use std::path::Path;
use tokio::io::AsyncRead;
mod content;
mod metadata;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
RocksDB(#[from] rocksdb::Error),
#[error(transparent)]
JSON(#[from] serde_json::Error),
#[error(transparent)]
IO(#[from] std::io::Error),
#[error{"task {0} not found"}]
TaskNotFound(String),
#[error{"piece {0} not found"}]
PieceNotFound(String),
#[error{"piece {0} state is failed"}]
PieceStateIsFailed(String),
#[error{"column family {0} not found"}]
ColumnFamilyNotFound(String),
#[error{"can not transit from {0} to {1}"}]
InvalidStateTransition(String, String),
#[error{"invalid state {0}"}]
InvalidState(String),
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct Storage {
metadata: metadata::Metadata,
content: content::Content,
}
impl Storage {
pub fn new(data_dir: &Path) -> Result<Self> {
let metadata = metadata::Metadata::new(data_dir)?;
let content = content::Content::new(data_dir)?;
Ok(Storage { metadata, content })
}
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
self.metadata.download_task_started(id, piece_length)
}
pub fn upload_task_finished(&self, id: &str) -> Result<()> {
self.metadata.upload_task_finished(id)
}
pub fn get_task(&self, id: &str) -> Result<Option<metadata::Task>> {
self.metadata.get_task(id)
}
pub fn download_piece_started(&self, task_id: &str, number: u32) -> Result<()> {
self.metadata.download_piece_started(task_id, number)
}
pub async fn download_piece_finished<R: AsyncRead + Unpin>(
&self,
task_id: &str,
offset: u64,
digest: &str,
reader: &mut R,
) -> Result<u64> {
let length = self.content.write_piece(task_id, offset, reader).await?;
self.metadata
.download_piece_finished(task_id, offset, length, digest)?;
Ok(length)
}
pub async fn upload_piece(&self, task_id: &str, number: u32) -> Result<impl AsyncRead> {
let id = self.metadata.piece_id(task_id, number);
match self.metadata.get_piece(&id)? {
Some(piece) => {
let reader = self
.content
.read_piece(task_id, piece.offset, piece.length)
.await?;
self.metadata.upload_piece_finished(&id)?;
Ok(reader)
}
None => Err(Error::PieceNotFound(id)),
}
}
pub fn get_piece(&self, task_id: &str, number: u32) -> Result<Option<metadata::Piece>> {
self.metadata
.get_piece(self.metadata.piece_id(task_id, number).as_str())
}
}