lib improve
This commit is contained in:
parent
d9b587c0a5
commit
1e31d74904
@ -162,7 +162,12 @@ impl Client {
|
|||||||
|
|
||||||
match serde_json::from_str(&text) {
|
match serde_json::from_str(&text) {
|
||||||
std::result::Result::Ok(json) => Ok(json),
|
std::result::Result::Ok(json) => Ok(json),
|
||||||
Err(e) => Err(anyhow::Error::new(e).context(text.trim().to_string())),
|
Err(err) => {
|
||||||
|
match serde_json::from_str::<types::response::SendyError>(&text) {
|
||||||
|
std::result::Result::Ok(json) => Err(anyhow::anyhow!("{:?}", json)),
|
||||||
|
Err(_) => Err(anyhow::Error::new(err).context(text.trim().to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
363
src/commands.rs
363
src/commands.rs
@ -1,363 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cmp::{max, min},
|
|
||||||
io::{stdout, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use aws_config::{environment::region, BehaviorVersion, Region, SdkConfig};
|
|
||||||
use aws_sdk_s3::{
|
|
||||||
config::Credentials, operation::upload_part, primitives::ByteStream, types::CompletedPart,
|
|
||||||
};
|
|
||||||
use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
|
|
||||||
use aws_smithy_types::byte_stream::Length;
|
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
use human_bytes::human_bytes;
|
|
||||||
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
|
|
||||||
use tokio::{fs::File, io::BufReader, sync::Mutex};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
client, constants::APP_VERSION, list_files, types, util::{check_job, file_detail, multipart_upload}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TargetFile {
|
|
||||||
pub file: PathBuf,
|
|
||||||
pub path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn list(prefix: &Option<String>, client: &mut client::Client) -> anyhow::Result<()> {
|
|
||||||
let res = list_files(Some(&prefix.clone().unwrap_or("".to_string())), client)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
res.file.iter().for_each(|f| {
|
|
||||||
let permission_string = if f.is_folder { "d" } else { "-" };
|
|
||||||
println!(
|
|
||||||
"{}\t{}\t{}\t{}",
|
|
||||||
permission_string,
|
|
||||||
human_bytes(f.size as u32),
|
|
||||||
f.last_modified,
|
|
||||||
f.path
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn upload(
|
|
||||||
file: &PathBuf,
|
|
||||||
prefix: &Option<String>,
|
|
||||||
recursive: &bool,
|
|
||||||
fake_size: &Option<u64>,
|
|
||||||
client: &mut client::Client,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
// is folder
|
|
||||||
if file.is_dir() && !*recursive {
|
|
||||||
println!("Use --recursive option for folder upload");
|
|
||||||
return Err(anyhow::anyhow!("Use --recursive option for folder upload"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut files = Vec::<TargetFile>::new();
|
|
||||||
|
|
||||||
if file.is_dir() && *recursive {
|
|
||||||
// upload folder
|
|
||||||
let mut dirs = Vec::<PathBuf>::new();
|
|
||||||
dirs.push(file.clone());
|
|
||||||
while let Some(dir) = dirs.pop() {
|
|
||||||
let entries = std::fs::read_dir(dir).unwrap();
|
|
||||||
for entry in entries {
|
|
||||||
let entry = entry.unwrap();
|
|
||||||
let path = entry.path();
|
|
||||||
if path.is_dir() {
|
|
||||||
dirs.push(path);
|
|
||||||
} else {
|
|
||||||
files.push(TargetFile {
|
|
||||||
file: path.clone(),
|
|
||||||
path: path
|
|
||||||
.strip_prefix(file)
|
|
||||||
.unwrap()
|
|
||||||
.to_str()
|
|
||||||
.expect("Invalid File Name")
|
|
||||||
.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// for file in files {
|
|
||||||
// println!("{:?}", file);
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
// file check
|
|
||||||
if !file.exists() {
|
|
||||||
println!("File not found: {:?}", file);
|
|
||||||
return Err(anyhow::anyhow!("File not found: {:?}", file));
|
|
||||||
}
|
|
||||||
files.push(TargetFile {
|
|
||||||
file: file.clone(),
|
|
||||||
path: file.file_name().unwrap().to_str().unwrap().to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg!(windows) {
|
|
||||||
// replase \ to /
|
|
||||||
files.iter_mut().for_each(|f| {
|
|
||||||
f.path = f.path.replace('\\', "/");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let req = types::request::CheckUploadRequest {
|
|
||||||
host_id: client.host_id.clone(),
|
|
||||||
path: prefix.clone().unwrap_or("".to_string()),
|
|
||||||
upload_id: "".to_string(),
|
|
||||||
file: files
|
|
||||||
.iter()
|
|
||||||
.map(|f| types::request::CheckUploadRequestFile {
|
|
||||||
path: f.path.clone(),
|
|
||||||
size: fake_size.unwrap_or(f.file.metadata().unwrap().len()) as i64,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let check_upload_res = client.check_upload(req).await.unwrap();
|
|
||||||
|
|
||||||
// println!("{:#?}", check_upload_res);
|
|
||||||
|
|
||||||
let token_res = client.get_upload_token().await.unwrap();
|
|
||||||
|
|
||||||
// println!("{:#?}", token_res);
|
|
||||||
|
|
||||||
let cledential = Credentials::new(
|
|
||||||
token_res.access_key_id.clone(),
|
|
||||||
token_res.secret_access_key.clone(),
|
|
||||||
Some(token_res.session_token.clone()),
|
|
||||||
None,
|
|
||||||
"2021-06-01",
|
|
||||||
);
|
|
||||||
let _config = aws_sdk_s3::Config::builder()
|
|
||||||
.behavior_version_latest()
|
|
||||||
.region(Region::new(check_upload_res.region.clone()))
|
|
||||||
.credentials_provider(cledential)
|
|
||||||
.force_path_style(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// if file_size > CHUNK_SIZE as u64 {
|
|
||||||
for (i, file) in files.iter().enumerate() {
|
|
||||||
println!("Multi Uploading: {:?}", file.file);
|
|
||||||
|
|
||||||
multipart_upload(
|
|
||||||
&token_res,
|
|
||||||
&check_upload_res.bucket,
|
|
||||||
&check_upload_res.file[i],
|
|
||||||
&check_upload_res.prefix,
|
|
||||||
&check_upload_res.region,
|
|
||||||
&check_upload_res.upload_id,
|
|
||||||
file.clone(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// for (i, file) in files.iter().enumerate() {
|
|
||||||
// println!("Uploading: {:?}", file.file);
|
|
||||||
// let stream = ByteStream::read_from()
|
|
||||||
// .path(file.file.clone())
|
|
||||||
// .offset(0)
|
|
||||||
// .length(Length::Exact(file_size))
|
|
||||||
// .build()
|
|
||||||
// .await
|
|
||||||
// .unwrap();
|
|
||||||
// let key =
|
|
||||||
// check_upload_res.prefix.to_owned() + check_upload_res.file[i].path.as_str();
|
|
||||||
// let _upload_res = s3_client
|
|
||||||
// .put_object()
|
|
||||||
// .bucket(check_upload_res.bucket.clone())
|
|
||||||
// .key(key)
|
|
||||||
// .body(stream)
|
|
||||||
// .send()
|
|
||||||
// .await
|
|
||||||
// .unwrap();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
match check_job(&check_upload_res.upload_id, client).await {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {:?}", e);
|
|
||||||
return Err(anyhow::anyhow!("Error: {:?}", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn download(
|
|
||||||
path: &String,
|
|
||||||
prefix: &Option<String>,
|
|
||||||
client: &mut client::Client,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let _file_name = path.split('/').last().unwrap();
|
|
||||||
let file_path =
|
|
||||||
path.split('/').collect::<Vec<&str>>()[0..path.split('/').count() - 1].join("/");
|
|
||||||
|
|
||||||
let list = list_files(Some(&file_path), client).await.unwrap();
|
|
||||||
|
|
||||||
let file = list
|
|
||||||
.file
|
|
||||||
.iter()
|
|
||||||
.find(|f| f.path == *path)
|
|
||||||
.expect("File not found");
|
|
||||||
|
|
||||||
let req = types::request::GetFileLinkRequest {
|
|
||||||
app_version: APP_VERSION.to_string(),
|
|
||||||
file: vec![types::request::GetFileLinkRequestFile {
|
|
||||||
path: path.to_string(),
|
|
||||||
size: file.size,
|
|
||||||
}],
|
|
||||||
host_id: client.host_id.clone(),
|
|
||||||
path: file_path,
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = client.get_download_link(req).await.unwrap();
|
|
||||||
|
|
||||||
// run aria2c
|
|
||||||
|
|
||||||
// TODO: Implement self implementation of multi connection download
|
|
||||||
let stdout = std::process::Command::new("aria2c")
|
|
||||||
.arg("-x16")
|
|
||||||
.arg("-s16")
|
|
||||||
.arg("-d")
|
|
||||||
.arg(".")
|
|
||||||
.arg(res.url)
|
|
||||||
.stdout(std::process::Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.expect("failed to execute process")
|
|
||||||
.stdout
|
|
||||||
.expect("failed to get stdout");
|
|
||||||
|
|
||||||
let reader = std::io::BufReader::new(stdout);
|
|
||||||
|
|
||||||
std::io::BufRead::lines(reader).for_each(|line| println!("{}", line.unwrap()));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete(
|
|
||||||
path: &String,
|
|
||||||
recursive: &bool,
|
|
||||||
client: &mut client::Client,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let file = file_detail(path, client).await.unwrap();
|
|
||||||
if file.is_folder && !*recursive {
|
|
||||||
println!("Use --recursive option for folder delete");
|
|
||||||
return Err(anyhow::anyhow!("Use --recursive option for folder delete"));
|
|
||||||
}
|
|
||||||
let req = types::request::DeleteFileRequest {
|
|
||||||
file: vec![types::request::FileModifyRequestFile {
|
|
||||||
last_modified: file.last_modified,
|
|
||||||
path: file.path,
|
|
||||||
version_id: file.version_id,
|
|
||||||
size: file.size,
|
|
||||||
}],
|
|
||||||
host_id: client.host_id.clone(),
|
|
||||||
prefix: "".to_string(),
|
|
||||||
trash: true,
|
|
||||||
};
|
|
||||||
let res = client.delete_file(req).await.unwrap();
|
|
||||||
|
|
||||||
match check_job(&res.key, client).await {
|
|
||||||
Ok(_) => {
|
|
||||||
println!("Deleted.");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {:?}", e);
|
|
||||||
Err(anyhow::anyhow!("Error: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn mkdir(
|
|
||||||
name: &String,
|
|
||||||
path: &Option<String>,
|
|
||||||
client: &mut client::Client,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if name.contains('/') {
|
|
||||||
println!("Please use --path option for set parent directory");
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"Please use --path option for set parent directory"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let req = types::request::CreateFolderRequest {
|
|
||||||
host_id: client.host_id.clone(),
|
|
||||||
name: name.clone(),
|
|
||||||
path: path.clone().unwrap_or("".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
match client.mkdir(req).await {
|
|
||||||
Ok(_) => {
|
|
||||||
println!("Created: {:?}", name);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {:?}", e);
|
|
||||||
Err(anyhow::anyhow!("Error: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn rename(
|
|
||||||
path: &String,
|
|
||||||
name: &String,
|
|
||||||
client: &mut client::Client,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if name.contains('/') {
|
|
||||||
println!("Can't use / in file name");
|
|
||||||
println!("Name should be file name only.");
|
|
||||||
return Err(anyhow::anyhow!("Can't use / in file name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_path =
|
|
||||||
path.split('/').collect::<Vec<&str>>()[0..path.split('/').count() - 1].join("/") + "/";
|
|
||||||
|
|
||||||
let list = list_files(Some(&file_path), client).await.unwrap();
|
|
||||||
|
|
||||||
let file = list
|
|
||||||
.file
|
|
||||||
.iter()
|
|
||||||
.find(|f| f.path == *path)
|
|
||||||
.expect("File not found");
|
|
||||||
|
|
||||||
let req = types::request::RenameFileRequest {
|
|
||||||
file: types::request::FileModifyRequestFile {
|
|
||||||
last_modified: file.last_modified.clone(),
|
|
||||||
path: file.path.clone(),
|
|
||||||
version_id: file.version_id.clone(),
|
|
||||||
size: file.size,
|
|
||||||
},
|
|
||||||
host_id: client.host_id.clone(),
|
|
||||||
name: name.clone(),
|
|
||||||
prefix: file_path,
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = client.rename_file(req).await.unwrap();
|
|
||||||
|
|
||||||
match check_job(&res.key, client).await {
|
|
||||||
Ok(_) => {
|
|
||||||
println!("Renamed.");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {:?}", e);
|
|
||||||
Err(anyhow::anyhow!("Error: {:?}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn info(path: &String, client: &mut client::Client) -> anyhow::Result<()> {
|
|
||||||
let req = types::request::FileDetailRequest {
|
|
||||||
host_id: client.host_id.clone(),
|
|
||||||
path: path.to_string(),
|
|
||||||
thumbnail_size: 130,
|
|
||||||
};
|
|
||||||
let res = client.file_detail(req).await.unwrap();
|
|
||||||
println!("{:#?}", res);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
403
src/lib.rs
403
src/lib.rs
@ -1,8 +1,10 @@
|
|||||||
use commands::list;
|
use aws_config::Region;
|
||||||
use util::list_files;
|
use aws_sdk_s3::config::Credentials;
|
||||||
|
use constants::APP_VERSION;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use util::{check_job, file_detail, list_files, multipart_upload, TargetFile};
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
mod commands;
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod types;
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
@ -18,18 +20,11 @@ impl RakutenDriveClient {
|
|||||||
}
|
}
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
&self,
|
&self,
|
||||||
prefix: &Option<String>,
|
prefix: Option<&str>,
|
||||||
) -> anyhow::Result<types::response::ListFilesResponse> {
|
) -> anyhow::Result<types::response::ListFilesResponse> {
|
||||||
list_files(
|
list_files(Some(prefix.unwrap_or("")), &self.client).await
|
||||||
Some(&prefix.clone().unwrap_or("".to_string())),
|
|
||||||
&self.client,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
pub async fn info(
|
pub async fn info(&self, path: &str) -> anyhow::Result<types::response::FileDetailResponse> {
|
||||||
&self,
|
|
||||||
path: &str,
|
|
||||||
) -> anyhow::Result<types::response::FileDetailResponse> {
|
|
||||||
let req = types::request::FileDetailRequest {
|
let req = types::request::FileDetailRequest {
|
||||||
host_id: self.client.host_id.clone(),
|
host_id: self.client.host_id.clone(),
|
||||||
path: path.to_string(),
|
path: path.to_string(),
|
||||||
@ -37,4 +32,386 @@ impl RakutenDriveClient {
|
|||||||
};
|
};
|
||||||
self.client.file_detail(req).await
|
self.client.file_detail(req).await
|
||||||
}
|
}
|
||||||
|
pub async fn upload(
|
||||||
|
&self,
|
||||||
|
file: &PathBuf,
|
||||||
|
prefix: Option<&str>,
|
||||||
|
recursive: bool,
|
||||||
|
fake_size: Option<u64>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
// is folder
|
||||||
|
if file.is_dir() && !recursive {
|
||||||
|
println!("Use --recursive option for folder upload");
|
||||||
|
return Err(anyhow::anyhow!("Use --recursive option for folder upload"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut files = Vec::<TargetFile>::new();
|
||||||
|
|
||||||
|
if file.is_dir() && recursive {
|
||||||
|
// upload folder
|
||||||
|
let mut dirs = Vec::<PathBuf>::new();
|
||||||
|
dirs.push(file.clone());
|
||||||
|
while let Some(dir) = dirs.pop() {
|
||||||
|
let entries = std::fs::read_dir(dir).unwrap();
|
||||||
|
for entry in entries {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
dirs.push(path);
|
||||||
|
} else {
|
||||||
|
files.push(TargetFile {
|
||||||
|
file: path.clone(),
|
||||||
|
path: path
|
||||||
|
.strip_prefix(file)
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.expect("Invalid File Name")
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for file in files {
|
||||||
|
// println!("{:?}", file);
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
// file check
|
||||||
|
if !file.exists() {
|
||||||
|
println!("File not found: {:?}", file);
|
||||||
|
return Err(anyhow::anyhow!("File not found: {:?}", file));
|
||||||
|
}
|
||||||
|
files.push(TargetFile {
|
||||||
|
file: file.clone(),
|
||||||
|
path: file.file_name().unwrap().to_str().unwrap().to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(windows) {
|
||||||
|
// replase \ to /
|
||||||
|
files.iter_mut().for_each(|f| {
|
||||||
|
f.path = f.path.replace('\\', "/");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in &files {
|
||||||
|
if (file_detail(&file.path, &self.client).await).is_ok() {
|
||||||
|
println!("File already exists.");
|
||||||
|
return Err(anyhow::anyhow!("File already exists."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let req = types::request::CheckUploadRequest {
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
path: prefix.unwrap_or("").to_string(),
|
||||||
|
upload_id: "".to_string(),
|
||||||
|
file: files
|
||||||
|
.iter()
|
||||||
|
.map(|f| types::request::CheckUploadRequestFile {
|
||||||
|
path: f.path.clone(),
|
||||||
|
size: fake_size.unwrap_or(f.file.metadata().unwrap().len()) as i64,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let check_upload_res = self.client.check_upload(req).await.unwrap();
|
||||||
|
|
||||||
|
// println!("{:#?}", check_upload_res);
|
||||||
|
|
||||||
|
let token_res = self.client.get_upload_token().await.unwrap();
|
||||||
|
|
||||||
|
// println!("{:#?}", token_res);
|
||||||
|
|
||||||
|
let cledential = Credentials::new(
|
||||||
|
token_res.access_key_id.clone(),
|
||||||
|
token_res.secret_access_key.clone(),
|
||||||
|
Some(token_res.session_token.clone()),
|
||||||
|
None,
|
||||||
|
"2021-06-01",
|
||||||
|
);
|
||||||
|
let _config = aws_sdk_s3::Config::builder()
|
||||||
|
.behavior_version_latest()
|
||||||
|
.region(Region::new(check_upload_res.region.clone()))
|
||||||
|
.credentials_provider(cledential)
|
||||||
|
.force_path_style(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// if file_size > CHUNK_SIZE as u64 {
|
||||||
|
for (i, file) in files.iter().enumerate() {
|
||||||
|
println!("Multi Uploading: {:?}", file.file);
|
||||||
|
|
||||||
|
multipart_upload(
|
||||||
|
&token_res,
|
||||||
|
&check_upload_res.bucket,
|
||||||
|
&check_upload_res.file[i],
|
||||||
|
&check_upload_res.prefix,
|
||||||
|
&check_upload_res.region,
|
||||||
|
&check_upload_res.upload_id,
|
||||||
|
file.clone(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// for (i, file) in files.iter().enumerate() {
|
||||||
|
// println!("Uploading: {:?}", file.file);
|
||||||
|
// let stream = ByteStream::read_from()
|
||||||
|
// .path(file.file.clone())
|
||||||
|
// .offset(0)
|
||||||
|
// .length(Length::Exact(file_size))
|
||||||
|
// .build()
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// let key =
|
||||||
|
// check_upload_res.prefix.to_owned() + check_upload_res.file[i].path.as_str();
|
||||||
|
// let _upload_res = s3_client
|
||||||
|
// .put_object()
|
||||||
|
// .bucket(check_upload_res.bucket.clone())
|
||||||
|
// .key(key)
|
||||||
|
// .body(stream)
|
||||||
|
// .send()
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
match check_job(&check_upload_res.upload_id, &self.client).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
Err(anyhow::anyhow!("Error: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn download(&self, path: &str, prefix: Option<&str>) -> anyhow::Result<()> {
|
||||||
|
let _file_name = path.split('/').last().unwrap();
|
||||||
|
let file_path =
|
||||||
|
path.split('/').collect::<Vec<&str>>()[0..path.split('/').count() - 1].join("/");
|
||||||
|
|
||||||
|
let file = match file_detail(path, &self.client).await {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let req = types::request::GetFileLinkRequest {
|
||||||
|
app_version: APP_VERSION.to_string(),
|
||||||
|
file: vec![types::request::GetFileLinkRequestFile {
|
||||||
|
path: path.to_string(),
|
||||||
|
size: file.size,
|
||||||
|
}],
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
path: file_path,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = self.client.get_download_link(req).await.unwrap();
|
||||||
|
|
||||||
|
// run aria2c
|
||||||
|
|
||||||
|
// TODO: Implement self implementation of multi connection download
|
||||||
|
let stdout = std::process::Command::new("aria2c")
|
||||||
|
.arg("-x16")
|
||||||
|
.arg("-s16")
|
||||||
|
.arg("-d")
|
||||||
|
.arg(".")
|
||||||
|
.arg(res.url)
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute process")
|
||||||
|
.stdout
|
||||||
|
.expect("failed to get stdout");
|
||||||
|
|
||||||
|
let reader = std::io::BufReader::new(stdout);
|
||||||
|
|
||||||
|
std::io::BufRead::lines(reader).for_each(|line| println!("{}", line.unwrap()));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn mkdir(&self, name: &str, path: Option<&str>) -> anyhow::Result<()> {
|
||||||
|
if name.contains('/') {
|
||||||
|
println!("Please use --path option for set parent directory");
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Please use --path option for set parent directory"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let req = types::request::CreateFolderRequest {
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
name: name.to_string(),
|
||||||
|
path: path.unwrap_or("").to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.client.mkdir(req).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Created: {:?}", name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
Err(anyhow::anyhow!("Error: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn rename(&self, path: &str, name: &str) -> anyhow::Result<()> {
|
||||||
|
if name.contains('/') {
|
||||||
|
println!("Can't use / in file name");
|
||||||
|
println!("Name should be file name only.");
|
||||||
|
return Err(anyhow::anyhow!("Can't use / in file name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_path =
|
||||||
|
path.split('/').collect::<Vec<&str>>()[0..path.split('/').count() - 1].join("/") + "/";
|
||||||
|
|
||||||
|
let file = match file_detail(path, &self.client).await {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let req = types::request::RenameFileRequest {
|
||||||
|
file: types::request::FileModifyRequestFile {
|
||||||
|
last_modified: file.last_modified.clone(),
|
||||||
|
path: file.path.clone(),
|
||||||
|
version_id: file.version_id.clone(),
|
||||||
|
size: file.size,
|
||||||
|
},
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
name: name.to_string(),
|
||||||
|
prefix: file_path,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = self.client.rename_file(req).await.unwrap();
|
||||||
|
|
||||||
|
match check_job(&res.key, &self.client).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Renamed.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
Err(anyhow::anyhow!("Error: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn move_file(&self, path: &str, dest: &str) -> anyhow::Result<()> {
|
||||||
|
if !dest.ends_with('/') {
|
||||||
|
println!("Destination should be directory.");
|
||||||
|
return Err(anyhow::anyhow!("Destination should be directory."));
|
||||||
|
}
|
||||||
|
let file = file_detail(path, &self.client).await.unwrap();
|
||||||
|
|
||||||
|
let file_name = path.split('/').last().unwrap();
|
||||||
|
let file_dir =
|
||||||
|
path.split('/').collect::<Vec<&str>>()[0..path.split('/').count() - 1].join("/") + "/";
|
||||||
|
|
||||||
|
if (file_detail((dest.to_string() + file_name).as_str(), &self.client).await).is_ok() {
|
||||||
|
println!("File already exists.");
|
||||||
|
return Err(anyhow::anyhow!("File already exists."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let req = types::request::MoveFileRequest {
|
||||||
|
file: vec![types::request::FileModifyRequestFile {
|
||||||
|
last_modified: file.last_modified,
|
||||||
|
path: file.path,
|
||||||
|
size: file.size,
|
||||||
|
version_id: file.version_id,
|
||||||
|
}],
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
prefix: file_dir.clone(),
|
||||||
|
target_id: self.client.host_id.clone(),
|
||||||
|
to_path: dest.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = self.client.move_file(req).await.unwrap();
|
||||||
|
|
||||||
|
match check_job(&res.key, &self.client).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Moved.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
Err(anyhow::anyhow!("Error: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&self, path: &str, recursive: &bool) -> anyhow::Result<()> {
|
||||||
|
let file = file_detail(path, &self.client).await.unwrap();
|
||||||
|
if file.is_folder && !*recursive {
|
||||||
|
println!("Use --recursive option for folder delete");
|
||||||
|
return Err(anyhow::anyhow!("Use --recursive option for folder delete"));
|
||||||
|
}
|
||||||
|
let req = types::request::DeleteFileRequest {
|
||||||
|
file: vec![types::request::FileModifyRequestFile {
|
||||||
|
last_modified: file.last_modified,
|
||||||
|
path: file.path,
|
||||||
|
version_id: file.version_id,
|
||||||
|
size: file.size,
|
||||||
|
}],
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
prefix: "".to_string(),
|
||||||
|
trash: true,
|
||||||
|
};
|
||||||
|
let res = self.client.delete_file(req).await.unwrap();
|
||||||
|
|
||||||
|
match check_job(&res.key, &self.client).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Deleted.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
Err(anyhow::anyhow!("Error: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn copy(&self, src: &str, dest: &str) -> anyhow::Result<()> {
|
||||||
|
if !dest.ends_with('/') {
|
||||||
|
println!("Destination should be directory.");
|
||||||
|
return Err(anyhow::anyhow!("Destination should be directory."));
|
||||||
|
}
|
||||||
|
let file_name = src.split('/').last().unwrap();
|
||||||
|
let file_dir =
|
||||||
|
src.split('/').collect::<Vec<&str>>()[0..src.split('/').count() - 1].join("/") + "/";
|
||||||
|
|
||||||
|
let file = file_detail(src, &self.client).await.unwrap();
|
||||||
|
|
||||||
|
if (file_detail((dest.to_string() + file_name).as_str(), &self.client).await).is_ok() {
|
||||||
|
println!("File already exists.");
|
||||||
|
return Err(anyhow::anyhow!("File already exists."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let req = types::request::CopyFileRequest {
|
||||||
|
file: vec![types::request::CopyFileRequestFile {
|
||||||
|
last_modified: file.last_modified,
|
||||||
|
path: file.path,
|
||||||
|
version_id: file.version_id,
|
||||||
|
size: file.size,
|
||||||
|
}],
|
||||||
|
host_id: self.client.host_id.clone(),
|
||||||
|
prefix: file_dir.clone(),
|
||||||
|
target_id: self.client.host_id.clone(),
|
||||||
|
to_path: dest.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = self.client.copy_file(req).await.unwrap();
|
||||||
|
|
||||||
|
match check_job(&res.key, &self.client).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Copied.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
Err(anyhow::anyhow!("Error: {:?}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
54
src/main.rs
54
src/main.rs
@ -12,20 +12,18 @@ use aws_sdk_s3::{
|
|||||||
use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
|
use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
|
||||||
use aws_smithy_types::byte_stream::Length;
|
use aws_smithy_types::byte_stream::Length;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use commands::{delete, download, info, list, mkdir, rename, upload};
|
|
||||||
use constants::REFRESH_TOKEN;
|
use constants::REFRESH_TOKEN;
|
||||||
use human_bytes::human_bytes;
|
use human_bytes::human_bytes;
|
||||||
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
|
||||||
|
use rakuten_drive_cui::RakutenDriveClient;
|
||||||
use tokio::{fs::File, io::BufReader, sync::Mutex};
|
use tokio::{fs::File, io::BufReader, sync::Mutex};
|
||||||
use types::response::ListFilesResponseFile;
|
use types::response::ListFilesResponseFile;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
mod commands;
|
mod constants;
|
||||||
mod types;
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
mod constants;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about=None)]
|
#[command(version, about, long_about=None)]
|
||||||
@ -67,7 +65,13 @@ enum Commands {
|
|||||||
prefix: Option<String>,
|
prefix: Option<String>,
|
||||||
},
|
},
|
||||||
#[clap(about = "Move file")]
|
#[clap(about = "Move file")]
|
||||||
Move {},
|
Move {
|
||||||
|
// Source file path
|
||||||
|
path: String,
|
||||||
|
|
||||||
|
// Destination folder path
|
||||||
|
dest: String,
|
||||||
|
},
|
||||||
#[clap(about = "Delete file")]
|
#[clap(about = "Delete file")]
|
||||||
Delete {
|
Delete {
|
||||||
path: String,
|
path: String,
|
||||||
@ -108,28 +112,44 @@ enum Commands {
|
|||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let mut client = client::Client::try_new(REFRESH_TOKEN.to_string())
|
let client = RakutenDriveClient::try_new(REFRESH_TOKEN.to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Commands::List { prefix } => list(prefix, &mut client).await,
|
Commands::List { prefix } => {
|
||||||
|
client.list(prefix.as_deref()).await.unwrap();
|
||||||
|
}
|
||||||
Commands::Upload {
|
Commands::Upload {
|
||||||
file,
|
file,
|
||||||
prefix,
|
prefix,
|
||||||
recursive,
|
recursive,
|
||||||
fake_size,
|
fake_size,
|
||||||
} => upload(file, prefix, recursive, fake_size, &mut client).await,
|
} => {
|
||||||
Commands::Download { path, prefix } => download(path, prefix, &mut client).await,
|
client
|
||||||
Commands::Move {} => {
|
.upload(file, prefix.as_deref(), *recursive, *fake_size)
|
||||||
todo!("Move");
|
.await.unwrap();
|
||||||
|
}
|
||||||
|
Commands::Download { path, prefix } => {
|
||||||
|
client.download(path.as_str(), prefix.as_deref()).await.unwrap();
|
||||||
|
}
|
||||||
|
Commands::Move { path, dest } => {
|
||||||
|
client.move_file(path, dest).await.unwrap();
|
||||||
|
}
|
||||||
|
Commands::Delete { path, recursive } => {
|
||||||
|
client.delete(path, recursive).await.unwrap();
|
||||||
|
}
|
||||||
|
Commands::Mkdir { name, path } => {
|
||||||
|
client.mkdir(name, path.as_deref()).await.unwrap();
|
||||||
}
|
}
|
||||||
Commands::Delete { path, recursive } => delete(path, recursive, &mut client).await,
|
|
||||||
Commands::Mkdir { name, path } => mkdir(name, path, &mut client).await,
|
|
||||||
Commands::Copy { src: _, dest: _ } => {
|
Commands::Copy { src: _, dest: _ } => {
|
||||||
todo!("Copy");
|
todo!("Copy");
|
||||||
}
|
}
|
||||||
Commands::Rename { path, name } => rename(path, name, &mut client).await,
|
Commands::Rename { path, name } => {
|
||||||
Commands::Info { path } => info(path, &mut client).await,
|
client.rename(path, name).await.unwrap();
|
||||||
|
}
|
||||||
|
Commands::Info { path } => {
|
||||||
|
client.info(path).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -66,7 +66,6 @@ pub struct MoveFileRequest {
|
|||||||
pub file: Vec<FileModifyRequestFile>,
|
pub file: Vec<FileModifyRequestFile>,
|
||||||
pub host_id: String,
|
pub host_id: String,
|
||||||
pub prefix: String,
|
pub prefix: String,
|
||||||
pub path: String,
|
|
||||||
pub target_id: String,
|
pub target_id: String,
|
||||||
pub to_path: String,
|
pub to_path: String,
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@ pub struct GetFileLinkTokenResponse {
|
|||||||
pub session_token: String,
|
pub session_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct GetFileLinkResponse {
|
pub struct GetFileLinkResponse {
|
||||||
@ -148,3 +147,32 @@ pub struct FileDetailResponseFile {
|
|||||||
#[serde(rename = "VersionID")]
|
#[serde(rename = "VersionID")]
|
||||||
pub version_id: String,
|
pub version_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct SendyError {
|
||||||
|
pub error: SendyErrorType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
pub enum SendyErrorType {
|
||||||
|
SendyErrFileNoFolder,
|
||||||
|
SendyErrFileNoSuchKey,
|
||||||
|
SendyErrFileAlreadyExistFileName,
|
||||||
|
SendyErrFileLongKey,
|
||||||
|
SendyErrExceededFolderMaxStorage,
|
||||||
|
SendyErrExceededTraffic,
|
||||||
|
SendyErrServer,
|
||||||
|
SendyErrAlreadyRunning,
|
||||||
|
SendyErrNoLinkToSave,
|
||||||
|
SendyErrPasswordNotMatch,
|
||||||
|
SendyErrLinkExpired,
|
||||||
|
SendyErrUninvitedUser,
|
||||||
|
SendyErrFileWrongPath,
|
||||||
|
SendyErrFileNoPermission,
|
||||||
|
SendyErrLinkInvalidPassword,
|
||||||
|
SendyErrShareDownwardShareExist,
|
||||||
|
SendyErrShareUpwardShareExist,
|
||||||
|
SendyErrShareFolderIncludedOrInclude,
|
||||||
|
}
|
||||||
|
@ -19,10 +19,15 @@ use tokio::{fs::File, io::BufReader, sync::Mutex};
|
|||||||
use crate::{constants::CHUNK_SIZE, types};
|
use crate::{constants::CHUNK_SIZE, types};
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{self},
|
client::{self},
|
||||||
commands::TargetFile,
|
|
||||||
types::response::ListFilesResponseFile,
|
types::response::ListFilesResponseFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TargetFile {
|
||||||
|
pub file: PathBuf,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn multipart_upload(
|
pub async fn multipart_upload(
|
||||||
token_res: &types::response::GetFileLinkTokenResponse,
|
token_res: &types::response::GetFileLinkTokenResponse,
|
||||||
bucket: &str,
|
bucket: &str,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user