Compare commits

..

2 commits

Author SHA1 Message Date
3b4cc49fd1 tasks + run "clippy fix" 2025-05-26 23:56:31 +00:00
9765227f0a code cleanup 2025-05-26 23:44:29 +00:00
8 changed files with 140 additions and 61 deletions

80
.vscode/tasks.json vendored Normal file
View 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"
// }
],
}

View file

@ -20,15 +20,15 @@ fn main() {
let share = NewShareRequest::new(Some("foo"), "", 10); let share = NewShareRequest::new(Some("foo"), "", 10);
let share = Share::create(&agent, &alias, share).unwrap(); 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(); 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) { for chunk in file.chunked(10 * 1024 * 1024) {
println!("chunk len: {}", chunk.bytes.len()); println!("chunk len: {}", chunk.bytes.len());
file.upload_chunk(&agent, chunk) file.upload_chunk(&agent, &chunk)
.inspect_err(|e| error!("error: {}", e)) .inspect_err(|e| error!("error: {e}"))
.unwrap(); .unwrap();
} }

View file

@ -3,7 +3,7 @@ use std::fmt::{Debug, Display};
use log::debug; use log::debug;
use ureq::RequestBuilder; use ureq::RequestBuilder;
use super::api::URI; use super::api::Uri;
#[derive(Debug)] #[derive(Debug)]
pub struct Alias { pub struct Alias {
@ -22,7 +22,7 @@ impl<B> SharryAlias for RequestBuilder<B> {
} }
impl Alias { 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 { Self {
api_uri: api_uri.into(), api_uri: api_uri.into(),
id: id.into(), id: id.into(),
@ -31,7 +31,7 @@ impl Alias {
pub(super) fn get_endpoint(&self, endpoint: impl Display + Debug) -> String { pub(super) fn get_endpoint(&self, endpoint: impl Display + Debug) -> String {
let uri = format!("{}/{}", self.api_uri, endpoint); let uri = format!("{}/{}", self.api_uri, endpoint);
debug!("endpoint uri: {:?}", uri); debug!("endpoint uri: {uri:?}");
uri uri
} }

View file

@ -1,11 +1,11 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub struct URI { pub struct Uri {
protocol: String, protocol: String,
base_url: String, base_url: String,
} }
impl URI { impl Uri {
pub fn with_protocol(protocol: impl Into<String>, base_url: impl Into<String>) -> Self { pub fn with_protocol(protocol: impl Into<String>, base_url: impl Into<String>) -> Self {
Self { Self {
protocol: protocol.into(), protocol: protocol.into(),
@ -14,15 +14,15 @@ impl URI {
} }
} }
impl From<&str> for URI { impl From<&str> for Uri {
fn from(value: &str) -> Self { fn from(value: &str) -> Self {
Self::with_protocol("https", value) Self::with_protocol("https", value)
} }
} }
impl Into<String> for URI { impl From<Uri> for String {
fn into(self) -> String { fn from(val: Uri) -> Self {
format!("{}://{}/api/v2", self.protocol, self.base_url) format!("{}://{}/api/v2", val.protocol, val.base_url)
} }
} }

View file

@ -1,7 +1,6 @@
use std::{ use std::{
fs, fs::File,
io::{self, Read, Seek}, io::{Read, Seek, SeekFrom},
mem,
path::PathBuf, path::PathBuf,
}; };
@ -23,7 +22,7 @@ impl<'t> ChunkedFile<'t> {
} }
} }
impl<'t> Iterator for ChunkedFile<'t> { impl Iterator for ChunkedFile<'_> {
type Item = Chunk; type Item = Chunk;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -32,29 +31,32 @@ impl<'t> Iterator for ChunkedFile<'t> {
self.cnum * csize self.cnum * csize
}; };
let mut f = fs::File::open(&self.path) let mut f = File::open(self.path)
.inspect_err(|e| error!("Error opening file: {}", e)) .inspect_err(|e| error!("Error opening file: {e}"))
.ok()?; .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 let read_len = f
.read(&mut buffer) .read(&mut bytes)
.inspect_err(|e| error!("Error reading file: {}", e)) .inspect_err(|e| error!("Error reading file: {e}"))
.ok()?; .ok()?;
buffer.truncate(read_len); bytes.truncate(read_len);
self.cnum += 1; self.cnum += 1;
Some(Self::Item { Some(Self::Item { offset, bytes }).filter(|c| !c.bytes.is_empty())
offset: offset.try_into().unwrap(),
bytes: buffer,
})
.filter(|c| c.bytes.len() > 0)
} }
} }
pub struct Chunk { pub struct Chunk {
pub offset: usize, pub offset: u64,
pub bytes: Vec<u8>, pub bytes: Vec<u8>,
} }
impl Chunk {
pub fn after(&self) -> u64 {
let len: u64 = self.bytes.len().try_into().unwrap();
self.offset + len
}
}

View file

@ -1,14 +1,12 @@
use std::{ use std::{
ffi::OsStr, ffi::OsStr,
fs, fs::metadata,
io::{self, Read, Seek}, io::{Read, Seek},
os::unix::fs::FileExt, path::PathBuf,
path::{Path, PathBuf},
str::FromStr,
}; };
use log::{debug, error}; use log::debug;
use ureq::http::StatusCode; use ureq::{Error::Other, http::StatusCode};
use super::{ use super::{
alias::{Alias, SharryAlias}, alias::{Alias, SharryAlias},
@ -19,7 +17,7 @@ use super::{
#[derive(Debug)] #[derive(Debug)]
pub struct File<'t> { pub struct File<'t> {
alias: &'t Alias, alias: &'t Alias,
file_path: PathBuf, path: PathBuf,
patch_uri: String, patch_uri: String,
} }
@ -27,10 +25,10 @@ impl<'t> File<'t> {
pub fn create( pub fn create(
http: &ureq::Agent, http: &ureq::Agent,
share: &'t Share, share: &'t Share,
file_path: impl Into<PathBuf>, path: impl Into<PathBuf>,
) -> Result<Self, ureq::Error> { ) -> Result<Self, ureq::Error> {
let file_path: PathBuf = file_path.into(); let path: PathBuf = path.into();
let filename = file_path let filename = path
.file_name() .file_name()
.and_then(OsStr::to_str) .and_then(OsStr::to_str)
.unwrap_or("file.bin"); .unwrap_or("file.bin");
@ -43,34 +41,34 @@ impl<'t> File<'t> {
.post(endpoint) .post(endpoint)
.sharry_header(share.alias) .sharry_header(share.alias)
.header("Sharry-File-Name", filename) .header("Sharry-File-Name", filename)
.header("Upload-Length", fs::metadata(&file_path)?.len()) .header("Upload-Length", metadata(&path)?.len())
.send_empty()?; .send_empty()?;
if res.status() != StatusCode::CREATED { if res.status() != StatusCode::CREATED {
return Err(ureq::Error::Other("unexpected response status".into())); return Err(Other("unexpected response status".into()));
} }
let location = res let location = res
.headers() .headers()
.get("Location") .get("Location")
.ok_or_else(|| ureq::Error::Other("Location header not found".into()))? .ok_or_else(|| Other("Location header not found".into()))?
.to_str() .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 { Ok(Self {
alias: share.alias, alias: share.alias,
file_path, path,
patch_uri: location.into(), patch_uri: location.into(),
}) })
} }
pub fn chunked(&self, chunk_size: usize) -> ChunkedFile { 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); debug!("upload uri: {:?}", self.patch_uri);
let res = http let res = http
@ -80,20 +78,20 @@ impl<'t> File<'t> {
.send(&chunk.bytes)?; .send(&chunk.bytes)?;
if res.status() != StatusCode::NO_CONTENT { 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 let offset = res
.headers() .headers()
.get("Upload-Offset") .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() .to_str()
.map_err(|_| ureq::Error::Other("Upload-Offset header invalid".into()))? .map_err(|_| Other("Upload-Offset header invalid".into()))?
.parse::<usize>() .parse::<u64>()
.map_err(|_| ureq::Error::Other("Upload-Offset header not an integer".into()))?; .map_err(|_| Other("Upload-Offset header not an integer".into()))?;
if chunk.offset + chunk.bytes.len() != offset { if chunk.after() != offset {
return Err(ureq::Error::Other("unexpected offset response".into())); return Err(Other("unexpected offset response".into()));
} }
Ok(()) Ok(())

View file

@ -7,6 +7,6 @@ mod file;
mod share; mod share;
pub use alias::Alias; pub use alias::Alias;
pub use api::{NewShareRequest, URI}; pub use api::{NewShareRequest, Uri};
pub use file::File; pub use file::File;
pub use share::Share; pub use share::Share;

View file

@ -1,4 +1,4 @@
use log::{debug, warn}; use log::debug;
use super::{ use super::{
alias::{Alias, SharryAlias}, alias::{Alias, SharryAlias},
@ -24,10 +24,9 @@ impl<'t> Share<'t> {
.body_mut() .body_mut()
.read_json::<NewShareResponse>()?; .read_json::<NewShareResponse>()?;
debug!("response: {:?}", res); debug!("response: {res:?}");
if !(res.success && (res.message == "Share created.")) { if !(res.success && (res.message == "Share created.")) {
warn!("unexpected json response");
return Err(ureq::Error::Other("unexpected json response".into())); return Err(ureq::Error::Other("unexpected json response".into()));
} }
@ -46,7 +45,7 @@ impl<'t> Share<'t> {
.body_mut() .body_mut()
.read_json::<NotifyShareResponse>()?; .read_json::<NotifyShareResponse>()?;
debug!("response: {:?}", res); debug!("response: {res:?}");
Ok(()) Ok(())
} }