client/src/storage/mod.rs

162 lines
5.6 KiB
Rust

/*
* Copyright 2023 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::utils::digest::{Algorithm, Digest};
use crate::{Error, Result};
use std::path::Path;
use tokio::io::AsyncRead;
pub mod content;
pub mod metadata;
// Storage is the storage of the task.
pub struct Storage {
// metadata implements the metadata storage.
metadata: metadata::Metadata,
// content implements the content storage.
content: content::Content,
}
// Storage implements the storage.
impl Storage {
// new returns a new 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 })
}
// download_task_started updates the metadata of the task when the task downloads started.
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
self.metadata.download_task_started(id, piece_length)
}
// set_task_content_length sets the content length of the task.
pub fn set_task_content_length(&self, id: &str, content_length: u64) -> Result<()> {
self.metadata.set_task_content_length(id, content_length)
}
// download_task_failed updates the metadata of the task when the task downloads failed.
pub fn download_task_failed(&self, id: &str) -> Result<()> {
self.metadata.download_task_failed(id)
}
// upload_task_finished updates the metadata of the task when task uploads finished.
pub fn upload_task_finished(&self, id: &str) -> Result<()> {
self.metadata.upload_task_finished(id)
}
// get_task returns the task metadata.
pub fn get_task(&self, id: &str) -> Result<Option<metadata::Task>> {
let task = self.metadata.get_task(id)?;
Ok(task)
}
// download_piece_started updates the metadata of the piece and writes
// the data of piece to file when the piece downloads started.
pub fn download_piece_started(&self, task_id: &str, number: u32) -> Result<()> {
self.metadata.download_piece_started(task_id, number)
}
// download_piece_from_source_finished is used for downloading piece from source.
pub async fn download_piece_from_source_finished<R: AsyncRead + Unpin + ?Sized>(
&self,
task_id: &str,
number: u32,
offset: u64,
length: u64,
reader: &mut R,
) -> Result<u64> {
let response = self.content.write_piece(task_id, offset, reader).await?;
let digest = Digest::new(Algorithm::Sha256, response.hash);
self.metadata.download_piece_finished(
task_id,
number,
offset,
length,
digest.to_string().as_str(),
)?;
Ok(length)
}
// download_piece_from_remote_peer_finished is used for downloading piece from remote peer.
pub async fn download_piece_from_remote_peer_finished<R: AsyncRead + Unpin + ?Sized>(
&self,
task_id: &str,
number: u32,
offset: u64,
expected_digest: &str,
reader: &mut R,
) -> Result<u64> {
let response = self.content.write_piece(task_id, offset, reader).await?;
let length = response.length;
let digest = Digest::new(Algorithm::Sha256, response.hash);
// Check the digest of the piece.
if expected_digest != digest.to_string() {
return Err(Error::PieceDigestMismatch());
}
self.metadata.download_piece_finished(
task_id,
number,
offset,
length,
digest.to_string().as_str(),
)?;
Ok(length)
}
// download_piece_failed updates the metadata of the piece when the piece downloads failed.
pub fn download_piece_failed(&self, task_id: &str, number: u32) -> Result<()> {
self.metadata.download_piece_failed(task_id, number)
}
// upload_piece updates the metadata of the piece and
// returns the data of the piece.
pub async fn upload_piece(&self, task_id: &str, number: u32) -> Result<impl AsyncRead> {
match self.metadata.get_piece(task_id, number)? {
Some(piece) => {
let reader = self
.content
.read_piece(task_id, piece.offset, piece.length)
.await?;
self.metadata.upload_piece_finished(task_id, number)?;
Ok(reader)
}
None => Err(Error::PieceNotFound(self.piece_id(task_id, number))),
}
}
// get_piece returns the piece metadata.
pub fn get_piece(&self, task_id: &str, number: u32) -> Result<Option<metadata::Piece>> {
let piece = self.metadata.get_piece(task_id, number)?;
Ok(piece)
}
// get_pieces returns the pieces metadata.
pub fn get_pieces(&self, task_id: &str) -> Result<Vec<metadata::Piece>> {
self.metadata.get_pieces(task_id)
}
// piece_id returns the piece id.
pub fn piece_id(&self, task_id: &str, number: u32) -> String {
self.metadata.piece_id(task_id, number)
}
}