Compare commits
2 commits
8ce3acde5e
...
3b4cc49fd1
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b4cc49fd1 | |||
| 9765227f0a |
8 changed files with 140 additions and 61 deletions
80
.vscode/tasks.json
vendored
Normal file
80
.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Project",
|
||||
"type": "cargo",
|
||||
"command": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$rustc",
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "Run Project",
|
||||
"type": "cargo",
|
||||
"command": "run",
|
||||
"dependsOn": [
|
||||
"Build Project"
|
||||
],
|
||||
"args": [
|
||||
"--bin=shrupl",
|
||||
"--package=shrupl"
|
||||
],
|
||||
"env": {
|
||||
"RUST_LOG": "shrupl=info",
|
||||
},
|
||||
"problemMatcher": "$rustc",
|
||||
"group": "none"
|
||||
},
|
||||
{
|
||||
"label": "Clippy Fix Project",
|
||||
"type": "cargo",
|
||||
"command": "clippy",
|
||||
"args": [
|
||||
"--fix",
|
||||
"--bin=shrupl",
|
||||
"--package=shrupl",
|
||||
"--allow-dirty",
|
||||
"--allow-staged",
|
||||
"--",
|
||||
"-Wclippy::pedantic"
|
||||
],
|
||||
"problemMatcher": "$rustc",
|
||||
"group": "build"
|
||||
},
|
||||
// {
|
||||
// "label": "Run Unit Tests",
|
||||
// "type": "cargo",
|
||||
// "command": "test",
|
||||
// "args": [
|
||||
// "--lib"
|
||||
// ],
|
||||
// "problemMatcher": "$rustc",
|
||||
// "group": "test"
|
||||
// },
|
||||
// {
|
||||
// "label": "Run Integration Tests",
|
||||
// "type": "cargo",
|
||||
// "command": "test",
|
||||
// "args": [
|
||||
// "--test",
|
||||
// "integration"
|
||||
// ],
|
||||
// "problemMatcher": "$rustc",
|
||||
// "group": "test"
|
||||
// },
|
||||
// {
|
||||
// "label": "Run All Tests",
|
||||
// "type": "shell",
|
||||
// "command": "echo All Tests successful!",
|
||||
// "dependsOn": [
|
||||
// "Run Unit Tests",
|
||||
// "Run Integration Tests"
|
||||
// ],
|
||||
// "dependsOrder": "sequence",
|
||||
// "group": "test"
|
||||
// }
|
||||
],
|
||||
}
|
||||
|
|
@ -20,15 +20,15 @@ fn main() {
|
|||
|
||||
let share = NewShareRequest::new(Some("foo"), "", 10);
|
||||
let share = Share::create(&agent, &alias, share).unwrap();
|
||||
info!("share: {:?}", share);
|
||||
info!("share: {share:?}");
|
||||
|
||||
let file = File::create(&agent, &share, "/lib/x86_64-linux-gnu/liblldb-14.so.1").unwrap();
|
||||
info!("file: {:?}", file);
|
||||
info!("file: {file:?}");
|
||||
|
||||
for chunk in file.chunked(10 * 1024 * 1024) {
|
||||
println!("chunk len: {}", chunk.bytes.len());
|
||||
file.upload_chunk(&agent, chunk)
|
||||
.inspect_err(|e| error!("error: {}", e))
|
||||
file.upload_chunk(&agent, &chunk)
|
||||
.inspect_err(|e| error!("error: {e}"))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::fmt::{Debug, Display};
|
|||
use log::debug;
|
||||
use ureq::RequestBuilder;
|
||||
|
||||
use super::api::URI;
|
||||
use super::api::Uri;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Alias {
|
||||
|
|
@ -22,7 +22,7 @@ impl<B> SharryAlias for RequestBuilder<B> {
|
|||
}
|
||||
|
||||
impl Alias {
|
||||
pub fn new(api_uri: URI, id: impl Into<String>) -> Self {
|
||||
pub fn new(api_uri: Uri, id: impl Into<String>) -> Self {
|
||||
Self {
|
||||
api_uri: api_uri.into(),
|
||||
id: id.into(),
|
||||
|
|
@ -31,7 +31,7 @@ impl Alias {
|
|||
|
||||
pub(super) fn get_endpoint(&self, endpoint: impl Display + Debug) -> String {
|
||||
let uri = format!("{}/{}", self.api_uri, endpoint);
|
||||
debug!("endpoint uri: {:?}", uri);
|
||||
debug!("endpoint uri: {uri:?}");
|
||||
|
||||
uri
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct URI {
|
||||
pub struct Uri {
|
||||
protocol: String,
|
||||
base_url: String,
|
||||
}
|
||||
|
||||
impl URI {
|
||||
impl Uri {
|
||||
pub fn with_protocol(protocol: impl Into<String>, base_url: impl Into<String>) -> Self {
|
||||
Self {
|
||||
protocol: protocol.into(),
|
||||
|
|
@ -14,15 +14,15 @@ impl URI {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&str> for URI {
|
||||
impl From<&str> for Uri {
|
||||
fn from(value: &str) -> Self {
|
||||
Self::with_protocol("https", value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for URI {
|
||||
fn into(self) -> String {
|
||||
format!("{}://{}/api/v2", self.protocol, self.base_url)
|
||||
impl From<Uri> for String {
|
||||
fn from(val: Uri) -> Self {
|
||||
format!("{}://{}/api/v2", val.protocol, val.base_url)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::{
|
||||
fs,
|
||||
io::{self, Read, Seek},
|
||||
mem,
|
||||
fs::File,
|
||||
io::{Read, Seek, SeekFrom},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
|
|
@ -23,7 +22,7 @@ impl<'t> ChunkedFile<'t> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'t> Iterator for ChunkedFile<'t> {
|
||||
impl Iterator for ChunkedFile<'_> {
|
||||
type Item = Chunk;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
|
@ -32,29 +31,32 @@ impl<'t> Iterator for ChunkedFile<'t> {
|
|||
self.cnum * csize
|
||||
};
|
||||
|
||||
let mut f = fs::File::open(&self.path)
|
||||
.inspect_err(|e| error!("Error opening file: {}", e))
|
||||
let mut f = File::open(self.path)
|
||||
.inspect_err(|e| error!("Error opening file: {e}"))
|
||||
.ok()?;
|
||||
f.seek(io::SeekFrom::Start(offset)).ok()?;
|
||||
f.seek(SeekFrom::Start(offset)).ok()?;
|
||||
|
||||
let mut buffer = vec![0; self.csize];
|
||||
let mut bytes = vec![0; self.csize];
|
||||
let read_len = f
|
||||
.read(&mut buffer)
|
||||
.inspect_err(|e| error!("Error reading file: {}", e))
|
||||
.read(&mut bytes)
|
||||
.inspect_err(|e| error!("Error reading file: {e}"))
|
||||
.ok()?;
|
||||
buffer.truncate(read_len);
|
||||
bytes.truncate(read_len);
|
||||
|
||||
self.cnum += 1;
|
||||
|
||||
Some(Self::Item {
|
||||
offset: offset.try_into().unwrap(),
|
||||
bytes: buffer,
|
||||
})
|
||||
.filter(|c| c.bytes.len() > 0)
|
||||
Some(Self::Item { offset, bytes }).filter(|c| !c.bytes.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Chunk {
|
||||
pub offset: usize,
|
||||
pub offset: u64,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub fn after(&self) -> u64 {
|
||||
let len: u64 = self.bytes.len().try_into().unwrap();
|
||||
self.offset + len
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
use std::{
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
io::{self, Read, Seek},
|
||||
os::unix::fs::FileExt,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
fs::metadata,
|
||||
io::{Read, Seek},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use log::{debug, error};
|
||||
use ureq::http::StatusCode;
|
||||
use log::debug;
|
||||
use ureq::{Error::Other, http::StatusCode};
|
||||
|
||||
use super::{
|
||||
alias::{Alias, SharryAlias},
|
||||
|
|
@ -19,7 +17,7 @@ use super::{
|
|||
#[derive(Debug)]
|
||||
pub struct File<'t> {
|
||||
alias: &'t Alias,
|
||||
file_path: PathBuf,
|
||||
path: PathBuf,
|
||||
patch_uri: String,
|
||||
}
|
||||
|
||||
|
|
@ -27,10 +25,10 @@ impl<'t> File<'t> {
|
|||
pub fn create(
|
||||
http: &ureq::Agent,
|
||||
share: &'t Share,
|
||||
file_path: impl Into<PathBuf>,
|
||||
path: impl Into<PathBuf>,
|
||||
) -> Result<Self, ureq::Error> {
|
||||
let file_path: PathBuf = file_path.into();
|
||||
let filename = file_path
|
||||
let path: PathBuf = path.into();
|
||||
let filename = path
|
||||
.file_name()
|
||||
.and_then(OsStr::to_str)
|
||||
.unwrap_or("file.bin");
|
||||
|
|
@ -43,34 +41,34 @@ impl<'t> File<'t> {
|
|||
.post(endpoint)
|
||||
.sharry_header(share.alias)
|
||||
.header("Sharry-File-Name", filename)
|
||||
.header("Upload-Length", fs::metadata(&file_path)?.len())
|
||||
.header("Upload-Length", metadata(&path)?.len())
|
||||
.send_empty()?;
|
||||
|
||||
if res.status() != StatusCode::CREATED {
|
||||
return Err(ureq::Error::Other("unexpected response status".into()));
|
||||
return Err(Other("unexpected response status".into()));
|
||||
}
|
||||
|
||||
let location = res
|
||||
.headers()
|
||||
.get("Location")
|
||||
.ok_or_else(|| ureq::Error::Other("Location header not found".into()))?
|
||||
.ok_or_else(|| Other("Location header not found".into()))?
|
||||
.to_str()
|
||||
.map_err(|_| ureq::Error::Other("Location header invalid".into()))?;
|
||||
.map_err(|_| Other("Location header invalid".into()))?;
|
||||
|
||||
debug!("location: {}", location);
|
||||
debug!("location: {location}");
|
||||
|
||||
Ok(Self {
|
||||
alias: share.alias,
|
||||
file_path,
|
||||
path,
|
||||
patch_uri: location.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chunked(&self, chunk_size: usize) -> ChunkedFile {
|
||||
ChunkedFile::new(&self.file_path, chunk_size)
|
||||
ChunkedFile::new(&self.path, chunk_size)
|
||||
}
|
||||
|
||||
pub fn upload_chunk(&self, http: &ureq::Agent, chunk: Chunk) -> Result<(), ureq::Error> {
|
||||
pub fn upload_chunk(&self, http: &ureq::Agent, chunk: &Chunk) -> Result<(), ureq::Error> {
|
||||
debug!("upload uri: {:?}", self.patch_uri);
|
||||
|
||||
let res = http
|
||||
|
|
@ -80,20 +78,20 @@ impl<'t> File<'t> {
|
|||
.send(&chunk.bytes)?;
|
||||
|
||||
if res.status() != StatusCode::NO_CONTENT {
|
||||
return Err(ureq::Error::Other("unexpected response status".into()));
|
||||
return Err(Other("unexpected response status".into()));
|
||||
}
|
||||
|
||||
let offset = res
|
||||
.headers()
|
||||
.get("Upload-Offset")
|
||||
.ok_or_else(|| ureq::Error::Other("Upload-Offset header not found".into()))?
|
||||
.ok_or_else(|| Other("Upload-Offset header not found".into()))?
|
||||
.to_str()
|
||||
.map_err(|_| ureq::Error::Other("Upload-Offset header invalid".into()))?
|
||||
.parse::<usize>()
|
||||
.map_err(|_| ureq::Error::Other("Upload-Offset header not an integer".into()))?;
|
||||
.map_err(|_| Other("Upload-Offset header invalid".into()))?
|
||||
.parse::<u64>()
|
||||
.map_err(|_| Other("Upload-Offset header not an integer".into()))?;
|
||||
|
||||
if chunk.offset + chunk.bytes.len() != offset {
|
||||
return Err(ureq::Error::Other("unexpected offset response".into()));
|
||||
if chunk.after() != offset {
|
||||
return Err(Other("unexpected offset response".into()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ mod file;
|
|||
mod share;
|
||||
|
||||
pub use alias::Alias;
|
||||
pub use api::{NewShareRequest, URI};
|
||||
pub use api::{NewShareRequest, Uri};
|
||||
pub use file::File;
|
||||
pub use share::Share;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use log::{debug, warn};
|
||||
use log::debug;
|
||||
|
||||
use super::{
|
||||
alias::{Alias, SharryAlias},
|
||||
|
|
@ -24,10 +24,9 @@ impl<'t> Share<'t> {
|
|||
.body_mut()
|
||||
.read_json::<NewShareResponse>()?;
|
||||
|
||||
debug!("response: {:?}", res);
|
||||
debug!("response: {res:?}");
|
||||
|
||||
if !(res.success && (res.message == "Share created.")) {
|
||||
warn!("unexpected json response");
|
||||
return Err(ureq::Error::Other("unexpected json response".into()));
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +45,7 @@ impl<'t> Share<'t> {
|
|||
.body_mut()
|
||||
.read_json::<NotifyShareResponse>()?;
|
||||
|
||||
debug!("response: {:?}", res);
|
||||
debug!("response: {res:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue