use std::env;
use std::sync::Arc;
use std::fs::Permissions;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use url::percent_encoding::{utf8_percent_encode, SIMPLE_ENCODE_SET};
use serde::{Serialize, Deserialize};
use futures::future::Future;
use osauth::{
services::ObjectStorageService, services::OBJECT_STORAGE, Adapter, AuthType, request::send_checked
};
use tokio::runtime::Runtime;
use crate::error::ChiconError;
use crate::{DirEntry, File, FileSystem, FileType};
define_encode_set! {
pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
}
pub struct SwiftFileSystem {
adapter: Adapter<ObjectStorageService>,
account: String,
container: String,
}
impl SwiftFileSystem {
pub fn new(
account: String,
container: String,
auth_url: String,
username: String,
password: String,
project_name: String,
) -> Result<Self, ChiconError> {
env::set_var("OS_AUTH_URL", auth_url);
env::set_var("OS_USERNAME", username);
env::set_var("OS_PASSWORD", password);
env::set_var("OS_PROJECT_NAME", project_name);
let mut runtime = Runtime::new().expect("cannot create a tokio runtime");
let adapter = Adapter::from_env(OBJECT_STORAGE)?;
runtime.block_on(adapter.put_empty(vec![account.clone(), container.clone()], None))?;
Ok(SwiftFileSystem { account, container, adapter })
}
pub fn new_from_env(account: String, container: String) -> Result<Self, ChiconError> {
let mut runtime = Runtime::new().expect("cannot create a tokio runtime");
let adapter = Adapter::from_env(OBJECT_STORAGE)?;
runtime.block_on(adapter.put_empty(vec![account.clone(), container.clone()], None))?;
Ok(SwiftFileSystem { account, container, adapter })
}
}
impl FileSystem for SwiftFileSystem {
type FSError = ChiconError;
type File = SwiftFile;
type DirEntry = SwiftDirEntry;
fn chmod<P: AsRef<Path>>(&self, path: P, perm: Permissions) -> Result<(), Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn create_file<P: AsRef<Path>>(&self, path: P) -> Result<Self::File, Self::FSError> {
let path = path.as_ref();
let object_path = utf8_percent_encode(
path.to_str().ok_or(ChiconError::BadPath)?,
QUERY_ENCODE_SET,
).to_string();
self.adapter.put_empty(&[&self.account, &self.container, &object_path], None).wait()?;
Ok(SwiftFile::new(self.adapter.clone(), self.account.clone(), self.container.clone(), PathBuf::from(path)))
}
fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<Self::File, Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn read_dir<P: AsRef<Path>>(&self, path: P) -> Result<Vec<Self::DirEntry>, Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::FSError> {
let path = path.as_ref();
unimplemented!()
}
fn rename<P: AsRef<Path>>(&self, from: P, to: P) -> Result<(), Self::FSError> {
let from = from.as_ref();
let to = to.as_ref();
unimplemented!()
}
}
pub struct SwiftFile {
adapter: Adapter<ObjectStorageService>,
account: String,
container: String,
filename: PathBuf,
content: Vec<u8>
}
impl SwiftFile {
fn new(adapter: Adapter<ObjectStorageService>, account: String, container: String, filename: PathBuf) -> Self {
SwiftFile {
adapter,
account,
container,
filename,
content: Vec::new()
}
}
}
impl File for SwiftFile {
type FSError = ChiconError;
fn sync_all(&mut self) -> Result<(), Self::FSError> {
let object_path = utf8_percent_encode(
self.filename.to_str().ok_or(ChiconError::BadPath)?,
QUERY_ENCODE_SET,
).to_string();
self.adapter.start_put(&[&self.account, &self.container, &object_path], None)
.map(|req_builder| req_builder.body(self.content.clone()))
.then(send_checked).wait()?;
Ok(())
}
}
impl Read for SwiftFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
let mut content_slice = self.content.as_slice();
let nb = content_slice.read(buf)?;
self.content = content_slice.to_vec();
Ok(nb)
}
}
impl Write for SwiftFile {
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
self.content.write(buf)
}
fn flush(&mut self) -> Result<(), std::io::Error> {
self.content.flush()
}
}
pub struct SwiftDirEntry(std::fs::DirEntry);
impl DirEntry for SwiftDirEntry {
type FSError = ChiconError;
fn path(&self) -> Result<PathBuf, Self::FSError> {
unimplemented!()
}
fn file_type(&self) -> Result<FileType, Self::FSError> {
unimplemented!()
}
}
#[derive(Serialize, Deserialize)]
struct Object {
name: String,
content_type: String,
bytes: i64,
last_modified: chrono::DateTime<chrono::Utc>,
hash: String,
sub_dir: String
}
#[derive(Serialize, Deserialize)]
struct ObjectQuery {
name: String,
count: i64,
bytes: i64
}
#[derive(Serialize, Deserialize)]
struct ContainerQuery {
limit: i32,
prefix: String,
marker: String,
end_marker: String,
headers: HashMap<String, String>,
}
#[cfg(test)]
mod tests {
use super::*;
use env_logger;
#[test]
fn test_create_file() {
env_logger::init();
let swift_fs = SwiftFileSystem::new_from_env(String::from("AUTH_tenantid"), String::from("testbnj")).expect("cannot create swift filesystem");
}
}