use crate::storage::DiskSpace;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io;
mod cloud;
pub(crate) use cloud::CloudService;
mod drive;
pub(crate) use drive::CloudDriveFactory;
mod cmds;
pub(crate) use cmds::add_all;
#[derive(Clone, Debug, PartialEq)]
pub struct AccessToken(String);
impl AccessToken {
pub(crate) fn new<S: Into<String>>(token: S) -> Self {
Self(token.into())
}
pub(crate) fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Deserialize)]
#[cfg_attr(test, derive(Debug, Serialize))]
pub struct ErrorResponse {
pub(crate) message: String,
#[serde(default)]
pub(crate) missing_data: Vec<String>,
}
#[derive(Debug, Serialize, PartialEq)]
#[cfg_attr(test, derive(Deserialize))]
pub struct LoginRequest {
#[serde(default = "HashMap::default")]
data: HashMap<String, String>,
}
#[derive(Deserialize)]
#[cfg_attr(test, derive(Debug, Serialize))]
pub struct LoginResponse {
username: String,
motd: Vec<String>,
}
#[derive(Deserialize)]
#[cfg_attr(test, derive(Debug, Serialize))]
pub struct DirectoryEntry {
filename: String,
mtime: u64,
length: u64,
}
#[derive(Deserialize)]
#[cfg_attr(test, derive(Debug, Serialize))]
pub struct GetFilesResponse {
files: Vec<DirectoryEntry>,
disk_quota: Option<DiskSpace>,
disk_free: Option<DiskSpace>,
}
#[derive(Debug, Default, PartialEq, Serialize)]
#[cfg_attr(test, derive(Deserialize))]
pub struct GetFileRequest {
get_content: bool,
get_readers: bool,
}
impl GetFileRequest {
fn with_get_content(mut self) -> Self {
self.get_content = true;
self
}
fn with_get_readers(mut self) -> Self {
self.get_readers = true;
self
}
}
#[derive(Default, Deserialize)]
#[cfg_attr(test, derive(Debug, PartialEq, Serialize))]
pub struct GetFileResponse {
content: Option<String>,
readers: Option<Vec<String>>,
}
impl GetFileResponse {
fn decoded_content(&self) -> io::Result<Option<Vec<u8>>> {
match self.content.as_ref() {
Some(content) => match base64::decode(content) {
Ok(content) => Ok(Some(content)),
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("File content is not properly base64-encoded: {}", e),
)),
},
None => Ok(None),
}
}
}
#[derive(Debug, Default, PartialEq, Serialize)]
#[cfg_attr(test, derive(Deserialize))]
pub struct PatchFileRequest {
content: Option<String>,
add_readers: Option<Vec<String>>,
remove_readers: Option<Vec<String>>,
}
impl PatchFileRequest {
fn with_content<C: AsRef<[u8]>>(mut self, content: C) -> Self {
self.content = Some(base64::encode(content));
self
}
#[cfg(test)]
fn with_add_readers<R: Into<Vec<String>>>(mut self, readers: R) -> Self {
self.add_readers = Some(readers.into());
self
}
#[cfg(test)]
fn with_remove_readers<R: Into<Vec<String>>>(mut self, readers: R) -> Self {
self.remove_readers = Some(readers.into());
self
}
}
pub type LoginResult = Result<LoginResponse, ErrorResponse>;
#[async_trait(?Send)]
pub trait Service {
async fn authenticate(&mut self, username: &str, password: &str) -> io::Result<AccessToken>;
async fn login(
&mut self,
access_token: &AccessToken,
request: &LoginRequest,
) -> io::Result<LoginResult>;
async fn get_files(
&mut self,
access_token: &AccessToken,
username: &str,
) -> io::Result<GetFilesResponse>;
async fn get_file(
&mut self,
access_token: &AccessToken,
username: &str,
filename: &str,
request: &GetFileRequest,
) -> io::Result<GetFileResponse>;
async fn patch_file(
&mut self,
access_token: &AccessToken,
username: &str,
filename: &str,
request: &PatchFileRequest,
) -> io::Result<()>;
async fn delete_file(
&mut self,
access_token: &AccessToken,
username: &str,
filename: &str,
) -> io::Result<()>;
}