2025-07-08 16:17:41 +00:00
|
|
|
use std::{
|
|
|
|
|
cell::{RefCell, RefMut},
|
|
|
|
|
collections::{HashMap, hash_map::Entry},
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-07 16:26:10 +00:00
|
|
|
use crate::{
|
2025-07-08 21:41:38 +00:00
|
|
|
Error, Result, error_response,
|
2025-07-08 16:17:41 +00:00
|
|
|
file::{self, FileTrait},
|
2025-07-07 19:33:31 +00:00
|
|
|
sharry::{AliasID, Client, FileID, ShareID, Uri, json},
|
2025-07-07 16:26:10 +00:00
|
|
|
};
|
|
|
|
|
|
2025-07-07 19:33:31 +00:00
|
|
|
use super::mock_ids::CheckID;
|
|
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
#[derive(Debug, Default)]
|
2025-07-08 16:17:41 +00:00
|
|
|
pub struct MockClient {
|
|
|
|
|
shares: RefCell<HashMap<String, MockShare>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
|
struct MockShare {
|
|
|
|
|
files: HashMap<String, MockFile>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct MockFile {
|
|
|
|
|
size: u64,
|
|
|
|
|
offset: u64,
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-09 15:56:35 +00:00
|
|
|
impl From<&file::Checked> for MockFile {
|
|
|
|
|
fn from(value: &file::Checked) -> Self {
|
2025-07-08 21:41:38 +00:00
|
|
|
Self {
|
2025-07-09 15:56:35 +00:00
|
|
|
size: value.get_size(),
|
|
|
|
|
offset: 0,
|
2025-07-08 21:41:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-08 16:17:41 +00:00
|
|
|
impl MockClient {
|
2025-07-09 15:56:35 +00:00
|
|
|
fn insert_share(&self, share_id: &ShareID, share: MockShare) -> Result<()> {
|
2025-07-08 16:17:41 +00:00
|
|
|
let mut shares = self.shares.borrow_mut();
|
|
|
|
|
|
|
|
|
|
let Entry::Vacant(entry) = shares.entry(share_id.to_string()) else {
|
2025-07-08 21:41:38 +00:00
|
|
|
return Err(error_response!("can't insert share {share_id:?}!"));
|
2025-07-08 16:17:41 +00:00
|
|
|
};
|
|
|
|
|
|
2025-07-09 15:56:35 +00:00
|
|
|
entry.insert(share);
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-09 15:56:35 +00:00
|
|
|
fn insert_file(&self, share_id: &ShareID, file_id: &FileID, file: MockFile) -> Result<()> {
|
2025-07-08 21:41:38 +00:00
|
|
|
let mut share = self.get_share(share_id)?;
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
let Entry::Vacant(entry) = share.files.entry(file_id.to_string()) else {
|
2025-07-08 21:41:38 +00:00
|
|
|
return Err(error_response!("can't insert file {file_id:?}!"));
|
2025-07-08 16:17:41 +00:00
|
|
|
};
|
|
|
|
|
|
2025-07-09 15:56:35 +00:00
|
|
|
entry.insert(file);
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
fn get_share<'t>(&'t self, share_id: &ShareID) -> Result<RefMut<'t, MockShare>> {
|
2025-07-08 16:17:41 +00:00
|
|
|
let share_id = &share_id.to_string();
|
|
|
|
|
let shares = self.shares.borrow_mut();
|
|
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
// check share exists
|
2025-07-08 16:17:41 +00:00
|
|
|
shares
|
|
|
|
|
.get(share_id)
|
2025-07-08 21:41:38 +00:00
|
|
|
.ok_or_else(|| error_response!("can't find share {share_id:?}!"))?;
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
Ok(RefMut::map(shares, |shares| {
|
2025-07-10 13:09:25 +00:00
|
|
|
shares.get_mut(share_id).unwrap()
|
2025-07-08 16:17:41 +00:00
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
fn get_file<'t>(
|
2025-07-08 16:17:41 +00:00
|
|
|
&'t self,
|
|
|
|
|
share_id: &ShareID,
|
|
|
|
|
file_id: &FileID,
|
|
|
|
|
) -> Result<RefMut<'t, MockFile>> {
|
|
|
|
|
let file_id = &file_id.to_string();
|
2025-07-08 21:41:38 +00:00
|
|
|
let share = self.get_share(share_id)?;
|
2025-07-08 16:17:41 +00:00
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
// check file exists
|
2025-07-08 16:17:41 +00:00
|
|
|
share
|
|
|
|
|
.files
|
|
|
|
|
.get(file_id)
|
2025-07-08 21:41:38 +00:00
|
|
|
.ok_or_else(|| error_response!("can't find file {file_id:?}!"))?;
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
Ok(RefMut::map(share, move |share| {
|
2025-07-10 13:09:25 +00:00
|
|
|
share.files.get_mut(file_id).unwrap()
|
2025-07-08 16:17:41 +00:00
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-07 16:26:10 +00:00
|
|
|
|
2025-07-07 19:33:31 +00:00
|
|
|
impl Client for MockClient {
|
2025-07-07 16:26:10 +00:00
|
|
|
fn share_create(
|
|
|
|
|
&self,
|
|
|
|
|
uri: &Uri,
|
|
|
|
|
alias_id: &AliasID,
|
2025-07-07 19:33:31 +00:00
|
|
|
_: json::NewShareRequest,
|
|
|
|
|
) -> Result<ShareID> {
|
|
|
|
|
(uri, alias_id).check()?;
|
2025-07-07 16:26:10 +00:00
|
|
|
|
2025-07-09 15:56:35 +00:00
|
|
|
let share_id = true.into();
|
|
|
|
|
self.insert_share(&share_id, MockShare::default())?;
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
Ok(share_id)
|
2025-07-07 16:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn share_notify(&self, uri: &Uri, alias_id: &AliasID, share_id: &ShareID) -> crate::Result<()> {
|
2025-07-07 19:33:31 +00:00
|
|
|
(uri, alias_id).check()?;
|
|
|
|
|
share_id.check()?;
|
2025-07-07 16:26:10 +00:00
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
let _share = self.get_share(share_id)?;
|
|
|
|
|
|
2025-07-07 16:26:10 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn file_create(
|
|
|
|
|
&self,
|
|
|
|
|
uri: &Uri,
|
|
|
|
|
alias_id: &AliasID,
|
|
|
|
|
share_id: &ShareID,
|
2025-07-08 16:17:41 +00:00
|
|
|
file: &file::Checked,
|
2025-07-07 19:33:31 +00:00
|
|
|
) -> Result<FileID> {
|
|
|
|
|
(uri, alias_id).check()?;
|
|
|
|
|
share_id.check()?;
|
2025-07-07 16:26:10 +00:00
|
|
|
|
2025-07-09 15:56:35 +00:00
|
|
|
let file_id = true.into();
|
|
|
|
|
self.insert_file(share_id, &file_id, file.into())?;
|
2025-07-08 16:17:41 +00:00
|
|
|
|
|
|
|
|
Ok(file_id)
|
2025-07-07 16:26:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn file_patch(
|
|
|
|
|
&self,
|
|
|
|
|
uri: &Uri,
|
|
|
|
|
alias_id: &AliasID,
|
|
|
|
|
share_id: &ShareID,
|
|
|
|
|
chunk: &file::Chunk,
|
2025-07-07 19:33:31 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
|
(uri, alias_id).check()?;
|
|
|
|
|
(share_id, chunk.get_file_id()).check()?;
|
2025-07-07 16:26:10 +00:00
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
let file = self.get_file(share_id, chunk.get_file_id())?;
|
|
|
|
|
|
|
|
|
|
if chunk.get_length() == 0 {
|
|
|
|
|
return Err(error_response!("chunk {chunk:?} empty!"));
|
|
|
|
|
} else if chunk.get_offset() % (1024 * 1024) != 0 {
|
|
|
|
|
return Err(error_response!("chunk {chunk:?} not aligned to a MiB!"));
|
|
|
|
|
} else if chunk.get_offset() != file.offset {
|
|
|
|
|
return Error::mismatch(file.offset, chunk.get_offset());
|
|
|
|
|
} else if file.offset + chunk.get_length() > file.size {
|
|
|
|
|
return Err(error_response!("chunk {chunk:?} too long!"));
|
|
|
|
|
}
|
2025-07-07 16:26:10 +00:00
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
let mut file = file;
|
|
|
|
|
file.offset += chunk.get_length();
|
2025-07-08 16:17:41 +00:00
|
|
|
|
2025-07-08 21:41:38 +00:00
|
|
|
Ok(())
|
2025-07-07 16:26:10 +00:00
|
|
|
}
|
|
|
|
|
}
|