reimplementation of sharry::file submodule
- compileable
This commit is contained in:
parent
7edadd7ca1
commit
88c6ce94de
10 changed files with 372 additions and 168 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -11,6 +11,7 @@
|
|||
"source.organizeImports": "explicit"
|
||||
},
|
||||
},
|
||||
"rust-analyzer.imports.prefix": "plain",
|
||||
// // override the default setting (`cargo check --all-targets`) which produces the following error
|
||||
// // "can't find crate for `test`" when the default compilation target is a no_std target
|
||||
// "rust-analyzer.checkOnSave.allTargets": false,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{
|
||||
fs,
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use log::{debug, trace};
|
||||
|
|
@ -9,22 +9,31 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use super::{
|
||||
cli::Cli,
|
||||
sharry::{Alias, File, Share},
|
||||
sharry::{Alias, FileChecked, FileUploading, Share},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct AppState {
|
||||
alias: Alias,
|
||||
share: Share,
|
||||
files: Vec<File>,
|
||||
checked: Vec<FileChecked>,
|
||||
uploading: Vec<FileUploading>,
|
||||
}
|
||||
|
||||
fn get_cachefile(args: &Cli) -> Option<PathBuf> {
|
||||
let file_name: PathBuf = dirs_next::cache_dir()?
|
||||
.join("shrupl")
|
||||
.join(format!("{}.json", args.get_hash()));
|
||||
|
||||
trace!("cachefile: {}", file_name.display());
|
||||
|
||||
Some(file_name)
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
fn load(file_name: impl AsRef<Path>) -> io::Result<Self> {
|
||||
let content = fs::read_to_string(file_name)?;
|
||||
let state = serde_json::from_str(&content).map_err(io::Error::other)?;
|
||||
|
||||
Ok(state)
|
||||
serde_json::from_str(&content).map_err(io::Error::other)
|
||||
}
|
||||
|
||||
fn save(&self, file_name: impl AsRef<Path>) -> io::Result<()> {
|
||||
|
|
@ -36,11 +45,12 @@ impl AppState {
|
|||
}
|
||||
|
||||
pub fn try_resume(args: &Cli) -> Option<Self> {
|
||||
let file_name = dirs_next::cache_dir()?
|
||||
.join("shrupl")
|
||||
.join(format!("{}.json", args.get_hash()));
|
||||
let file_name = get_cachefile(args)?;
|
||||
|
||||
trace!("loading from {}", file_name.display());
|
||||
// let content = fs::read_to_string(&file_name).ok()?;
|
||||
// serde_json::from_str(&content)
|
||||
// .inspect_err(|e| debug!("could not resume from {}: {e}", &file_name.display()))
|
||||
// .ok()
|
||||
|
||||
Self::load(&file_name)
|
||||
.inspect_err(|e| debug!("could not resume from {}: {e}", file_name.display()))
|
||||
|
|
|
|||
10
src/cli.rs
10
src/cli.rs
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use clap::{Parser, builder::PossibleValuesParser};
|
||||
|
||||
use super::sharry::{Alias, File, NewShareRequest, Uri};
|
||||
use super::sharry::{Alias, FileChecked, NewShareRequest, Uri};
|
||||
|
||||
#[derive(Parser, Debug, Hash)]
|
||||
#[command(version, about, long_about = None)]
|
||||
|
|
@ -50,15 +50,15 @@ pub struct Cli {
|
|||
|
||||
/// Files to upload to the new share
|
||||
#[arg(value_name = "FILE", required = true, value_parser = parse_sharry_file)]
|
||||
pub files: Vec<File>,
|
||||
pub files: Vec<FileChecked>,
|
||||
}
|
||||
|
||||
fn parse_seconds(data: &str) -> Result<Duration, String> {
|
||||
data.parse().or(Ok(0)).map(Duration::from_secs)
|
||||
}
|
||||
|
||||
fn parse_sharry_file(data: &str) -> Result<File, String> {
|
||||
File::new(data).map_err(|e| e.to_string())
|
||||
fn parse_sharry_file(data: &str) -> Result<FileChecked, String> {
|
||||
FileChecked::new(data).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
|
|
@ -76,7 +76,7 @@ impl Cli {
|
|||
|
||||
pub fn get_hash(&self) -> String {
|
||||
let file_refs = {
|
||||
let mut refs: Vec<_> = self.files.iter().map(File::get_path).collect();
|
||||
let mut refs: Vec<_> = self.files.iter().map(FileChecked::get_path).collect();
|
||||
refs.sort_unstable();
|
||||
|
||||
refs
|
||||
|
|
|
|||
18
src/main.rs
18
src/main.rs
|
|
@ -31,18 +31,18 @@ fn main() {
|
|||
info!("share: {share:?}");
|
||||
|
||||
for file in args.files {
|
||||
let file = file.create(&agent, &alias, &share).unwrap();
|
||||
let file = file.start_upload(&agent, &alias, &share).unwrap();
|
||||
info!("file: {file:?}");
|
||||
|
||||
for chunk in file.chunked(args.chunk_size * 1024 * 1024).seek(0) {
|
||||
info!("chunk: {chunk:?}");
|
||||
// for chunk in file.chunked(args.chunk_size * 1024 * 1024).seek(0) {
|
||||
// info!("chunk: {chunk:?}");
|
||||
|
||||
file.upload_chunk(&agent, &alias, &chunk)
|
||||
.unwrap_or_else(|e| {
|
||||
error!("error: {e}");
|
||||
panic!("{e}");
|
||||
});
|
||||
}
|
||||
// file.upload_chunk(&agent, &alias, &chunk)
|
||||
// .unwrap_or_else(|e| {
|
||||
// error!("error: {e}");
|
||||
// panic!("{e}");
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
share.notify(&agent, &alias).unwrap();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ use std::{
|
|||
};
|
||||
|
||||
use log::error;
|
||||
// use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileChunks<'t> {
|
||||
file_path: &'t Path,
|
||||
offset: u64,
|
||||
|
|
@ -37,19 +39,22 @@ impl Iterator for FileChunks<'_> {
|
|||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let offset = self.offset;
|
||||
|
||||
let mut f = File::open(self.file_path)
|
||||
.inspect_err(|e| error!("Error opening file: {e}"))
|
||||
.ok()?;
|
||||
f.seek(SeekFrom::Start(offset)).ok()?;
|
||||
let bytes = {
|
||||
let mut f = File::open(self.file_path)
|
||||
.inspect_err(|e| error!("Error opening file: {e}"))
|
||||
.ok()?;
|
||||
f.seek(SeekFrom::Start(offset)).ok()?;
|
||||
|
||||
let mut bytes = vec![0; self.chunk_size];
|
||||
let read_len = (f.read(&mut bytes))
|
||||
.inspect_err(|e| error!("Error reading file: {e}"))
|
||||
.ok()?;
|
||||
bytes.truncate(read_len);
|
||||
let mut bytes = vec![0; self.chunk_size];
|
||||
let read_len = (f.read(&mut bytes))
|
||||
.inspect_err(|e| error!("Error reading file: {e}"))
|
||||
.ok()?;
|
||||
bytes.truncate(read_len);
|
||||
|
||||
let read_len: u64 = read_len
|
||||
.try_into()
|
||||
bytes
|
||||
};
|
||||
|
||||
let read_len: u64 = (bytes.len().try_into())
|
||||
.inspect_err(|e| error!("Error converting to u64: {e}"))
|
||||
.ok()?;
|
||||
self.offset += read_len;
|
||||
142
src/sharry/file/_mod.rs
Normal file
142
src/sharry/file/_mod.rs
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
mod chunks;
|
||||
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs::{canonicalize, metadata},
|
||||
hash::{Hash, Hasher},
|
||||
io::{self, ErrorKind},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use log::{debug, error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ureq::{Error::Other, http::StatusCode};
|
||||
|
||||
use super::{
|
||||
alias::{Alias, SharryAlias},
|
||||
share::Share,
|
||||
};
|
||||
pub use chunks::{Chunk, FileChunks};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct File {
|
||||
abs_path: PathBuf,
|
||||
name: String,
|
||||
size: u64,
|
||||
patch_uri: Option<String>,
|
||||
}
|
||||
|
||||
impl Hash for File {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.abs_path.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for File {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.abs_path == other.abs_path
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for File {}
|
||||
|
||||
impl File {
|
||||
pub fn new(path: impl AsRef<Path>) -> io::Result<Self> {
|
||||
let abs_path = canonicalize(path)?;
|
||||
|
||||
let m = metadata(&abs_path)?;
|
||||
if !m.is_file() {
|
||||
return Err(io::Error::new(ErrorKind::NotFound, "not a file"));
|
||||
}
|
||||
|
||||
let name = (abs_path.file_name().and_then(OsStr::to_str))
|
||||
.ok_or_else(|| io::Error::new(ErrorKind::NotFound, "bad file name"))?
|
||||
.to_string();
|
||||
|
||||
Ok(Self {
|
||||
abs_path,
|
||||
name,
|
||||
size: m.len(),
|
||||
patch_uri: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_path(&self) -> &Path {
|
||||
&self.abs_path
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
self,
|
||||
http: &ureq::Agent,
|
||||
alias: &Alias,
|
||||
share: &Share,
|
||||
) -> Result<Self, ureq::Error> {
|
||||
if self.patch_uri.is_some() {
|
||||
return Err(Other("patch_uri already set".into()));
|
||||
}
|
||||
|
||||
let endpoint = alias.get_endpoint(format!("alias/upload/{}/files/tus", share.id));
|
||||
|
||||
let res = (http.post(endpoint))
|
||||
.sharry_header(alias)
|
||||
.header("Sharry-File-Name", &self.name)
|
||||
.header("Upload-Length", self.size)
|
||||
.send_empty()?;
|
||||
|
||||
if res.status() != StatusCode::CREATED {
|
||||
return Err(Other("unexpected response status".into()));
|
||||
}
|
||||
|
||||
let location = (res.headers().get("Location"))
|
||||
.ok_or_else(|| Other("Location header not found".into()))?
|
||||
.to_str()
|
||||
.map_err(|_| Other("Location header invalid".into()))?
|
||||
.to_string();
|
||||
|
||||
debug!("received uri: {location}");
|
||||
|
||||
Ok(Self {
|
||||
abs_path: self.abs_path,
|
||||
name: self.name,
|
||||
size: self.size,
|
||||
patch_uri: Some(location),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chunked(&self, chunk_size: usize) -> FileChunks {
|
||||
FileChunks::new(&self.abs_path, chunk_size)
|
||||
}
|
||||
|
||||
pub fn upload_chunk(
|
||||
&self,
|
||||
http: &ureq::Agent,
|
||||
alias: &Alias,
|
||||
chunk: &Chunk,
|
||||
) -> Result<(), ureq::Error> {
|
||||
let patch_uri = (self.patch_uri.as_ref()).ok_or_else(|| Other("unset patch_uri".into()))?;
|
||||
|
||||
debug!("upload uri: {patch_uri:?}");
|
||||
|
||||
let res = (http.patch(patch_uri))
|
||||
.sharry_header(alias)
|
||||
.header("Upload-Offset", chunk.offset)
|
||||
.send(&chunk.bytes)?;
|
||||
|
||||
if res.status() != StatusCode::NO_CONTENT {
|
||||
return Err(Other("unexpected response status".into()));
|
||||
}
|
||||
|
||||
let offset = (res.headers().get("Upload-Offset"))
|
||||
.ok_or_else(|| Other("Upload-Offset header not found".into()))?
|
||||
.to_str()
|
||||
.map_err(|e| Other(e.into()))?
|
||||
.parse::<u64>()
|
||||
.map_err(|e| Other(e.into()))?;
|
||||
|
||||
if chunk.after() != offset {
|
||||
return Err(Other("unexpected offset response".into()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
80
src/sharry/file/checked.rs
Normal file
80
src/sharry/file/checked.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use std::{
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
io::{self, ErrorKind},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ureq::http::StatusCode;
|
||||
|
||||
use super::{Alias, FileUploading, Share, SharryAlias};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
|
||||
pub struct FileChecked {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl FileChecked {
|
||||
pub fn new(value: impl AsRef<Path>) -> io::Result<Self> {
|
||||
let meta = fs::metadata(&value)?;
|
||||
if meta.is_file() {
|
||||
Ok(Self {
|
||||
path: fs::canonicalize(&value)?,
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"Not a regular file",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn start_upload(
|
||||
self,
|
||||
http: &ureq::Agent,
|
||||
alias: &Alias,
|
||||
share: &Share,
|
||||
) -> io::Result<FileUploading> {
|
||||
let size = usize::try_from(fs::metadata(&self.path)?.len()).map_err(io::Error::other)?;
|
||||
|
||||
let res = {
|
||||
let endpoint = alias.get_endpoint(format!("alias/upload/{}/files/tus", share.id));
|
||||
|
||||
let name = (self.path.file_name().and_then(OsStr::to_str))
|
||||
.ok_or_else(|| io::Error::new(ErrorKind::NotFound, "bad file name"))?
|
||||
.to_string();
|
||||
|
||||
(http.post(endpoint))
|
||||
.sharry_header(alias)
|
||||
.header("Sharry-File-Name", &name)
|
||||
.header("Upload-Length", size)
|
||||
.send_empty()
|
||||
.map_err(io::Error::other)?
|
||||
};
|
||||
|
||||
if res.status() != StatusCode::CREATED {
|
||||
return Err(io::Error::other("unexpected response status"));
|
||||
}
|
||||
|
||||
let location = (res.headers().get("Location"))
|
||||
.ok_or_else(|| io::Error::other("Location header not found"))?
|
||||
.to_str()
|
||||
.map_err(|_| io::Error::other("Location header invalid"))?
|
||||
.to_string();
|
||||
|
||||
debug!("patch uri: {location}");
|
||||
|
||||
Ok(FileUploading {
|
||||
path: self.path,
|
||||
size,
|
||||
uri: location,
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +1,7 @@
|
|||
mod chunks;
|
||||
mod checked;
|
||||
mod uploading;
|
||||
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs::{canonicalize, metadata},
|
||||
hash::{Hash, Hasher},
|
||||
io::{self, ErrorKind},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
pub use checked::FileChecked;
|
||||
pub use uploading::FileUploading;
|
||||
|
||||
use log::{debug, error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ureq::{Error::Other, http::StatusCode};
|
||||
|
||||
use super::{
|
||||
alias::{Alias, SharryAlias},
|
||||
share::Share,
|
||||
};
|
||||
pub use chunks::{Chunk, FileChunks};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct File {
|
||||
abs_path: PathBuf,
|
||||
name: String,
|
||||
size: u64,
|
||||
patch_uri: Option<String>,
|
||||
}
|
||||
|
||||
impl Hash for File {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.abs_path.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn new(path: impl AsRef<Path>) -> io::Result<Self> {
|
||||
let abs_path = canonicalize(path)?;
|
||||
|
||||
let m = metadata(&abs_path)?;
|
||||
if !m.is_file() {
|
||||
return Err(io::Error::new(ErrorKind::NotFound, "not a file"));
|
||||
}
|
||||
|
||||
let name = (abs_path.file_name().and_then(OsStr::to_str))
|
||||
.ok_or_else(|| io::Error::new(ErrorKind::NotFound, "bad file name"))?
|
||||
.to_string();
|
||||
|
||||
Ok(Self {
|
||||
abs_path,
|
||||
name,
|
||||
size: m.len(),
|
||||
patch_uri: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_path(&self) -> &Path {
|
||||
&self.abs_path
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
self,
|
||||
http: &ureq::Agent,
|
||||
alias: &Alias,
|
||||
share: &Share,
|
||||
) -> Result<Self, ureq::Error> {
|
||||
if self.patch_uri.is_some() {
|
||||
return Err(Other("patch_uri already set".into()));
|
||||
}
|
||||
|
||||
let endpoint = alias.get_endpoint(format!("alias/upload/{}/files/tus", share.id));
|
||||
|
||||
let res = (http.post(endpoint))
|
||||
.sharry_header(alias)
|
||||
.header("Sharry-File-Name", &self.name)
|
||||
.header("Upload-Length", self.size)
|
||||
.send_empty()?;
|
||||
|
||||
if res.status() != StatusCode::CREATED {
|
||||
return Err(Other("unexpected response status".into()));
|
||||
}
|
||||
|
||||
let location = (res.headers().get("Location"))
|
||||
.ok_or_else(|| Other("Location header not found".into()))?
|
||||
.to_str()
|
||||
.map_err(|_| Other("Location header invalid".into()))?
|
||||
.to_string();
|
||||
|
||||
debug!("received uri: {location}");
|
||||
|
||||
Ok(Self {
|
||||
abs_path: self.abs_path,
|
||||
name: self.name,
|
||||
size: self.size,
|
||||
patch_uri: Some(location),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chunked(&self, chunk_size: usize) -> FileChunks {
|
||||
FileChunks::new(&self.abs_path, chunk_size)
|
||||
}
|
||||
|
||||
pub fn upload_chunk(
|
||||
&self,
|
||||
http: &ureq::Agent,
|
||||
alias: &Alias,
|
||||
chunk: &Chunk,
|
||||
) -> Result<(), ureq::Error> {
|
||||
let patch_uri = (self.patch_uri.as_ref()).ok_or_else(|| Other("unset patch_uri".into()))?;
|
||||
|
||||
debug!("upload uri: {patch_uri:?}");
|
||||
|
||||
let res = (http.patch(patch_uri))
|
||||
.sharry_header(alias)
|
||||
.header("Upload-Offset", chunk.offset)
|
||||
.send(&chunk.bytes)?;
|
||||
|
||||
if res.status() != StatusCode::NO_CONTENT {
|
||||
return Err(Other("unexpected response status".into()));
|
||||
}
|
||||
|
||||
let offset = (res.headers().get("Upload-Offset"))
|
||||
.ok_or_else(|| Other("Upload-Offset header not found".into()))?
|
||||
.to_str()
|
||||
.map_err(|e| Other(e.into()))?
|
||||
.parse::<u64>()
|
||||
.map_err(|e| Other(e.into()))?;
|
||||
|
||||
if chunk.after() != offset {
|
||||
return Err(Other("unexpected offset response".into()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
use super::{Alias, Share, alias::SharryAlias};
|
||||
|
|
|
|||
93
src/sharry/file/uploading.rs
Normal file
93
src/sharry/file/uploading.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{self, Read, Seek, SeekFrom},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ureq::{
|
||||
Error::Other,
|
||||
http::{HeaderValue, StatusCode},
|
||||
};
|
||||
|
||||
use super::{Alias, SharryAlias};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FileUploading {
|
||||
pub(super) path: PathBuf,
|
||||
pub(super) size: usize,
|
||||
pub(super) uri: String,
|
||||
pub(super) offset: usize,
|
||||
}
|
||||
|
||||
pub enum UploadError {
|
||||
FileIO(io::Error),
|
||||
Request,
|
||||
ResponseStatus,
|
||||
ResponseOffset,
|
||||
}
|
||||
|
||||
pub enum ChunkState {
|
||||
Ok(FileUploading),
|
||||
Err(FileUploading, UploadError),
|
||||
Finished,
|
||||
}
|
||||
|
||||
impl FileUploading {
|
||||
fn read_chunk(&self, chunk_size: usize) -> io::Result<Vec<u8>> {
|
||||
let offset = u64::try_from(self.offset).map_err(io::Error::other)?;
|
||||
|
||||
let mut f = File::open(&self.path)?;
|
||||
f.seek(SeekFrom::Start(offset))?;
|
||||
|
||||
let mut bytes = vec![0; chunk_size];
|
||||
let read_len = f.read(&mut bytes)?;
|
||||
bytes.truncate(read_len);
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub fn upload_chunk(
|
||||
mut self,
|
||||
http: &ureq::Agent,
|
||||
alias: &Alias,
|
||||
chunk_size: usize,
|
||||
) -> ChunkState {
|
||||
let chunk = match self.read_chunk(chunk_size) {
|
||||
Err(e) => return ChunkState::Err(self, UploadError::FileIO(e)),
|
||||
Ok(value) => value,
|
||||
};
|
||||
|
||||
let Ok(res) = (http.patch(&self.uri))
|
||||
.sharry_header(alias)
|
||||
.header("Upload-Offset", self.offset)
|
||||
.send(&chunk)
|
||||
else {
|
||||
return ChunkState::Err(self, UploadError::Request);
|
||||
};
|
||||
|
||||
if res.status() != StatusCode::NO_CONTENT {
|
||||
return ChunkState::Err(self, UploadError::ResponseStatus);
|
||||
}
|
||||
|
||||
let Some(Ok(Ok(res_offset))) = (res.headers().get("Upload-Offset"))
|
||||
.map(HeaderValue::to_str)
|
||||
.map(|v| v.map(str::parse::<usize>))
|
||||
else {
|
||||
return ChunkState::Err(self, UploadError::ResponseOffset);
|
||||
};
|
||||
|
||||
if self.offset + chunk.len() != res_offset {
|
||||
return ChunkState::Err(self, UploadError::ResponseOffset);
|
||||
}
|
||||
|
||||
self.offset += res_offset;
|
||||
|
||||
if self.offset == self.size {
|
||||
return ChunkState::Finished;
|
||||
}
|
||||
|
||||
ChunkState::Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,5 +7,5 @@ mod share;
|
|||
|
||||
pub use alias::Alias;
|
||||
pub use api::{NewShareRequest, Uri};
|
||||
pub use file::File;
|
||||
pub use file::{FileChecked, FileUploading};
|
||||
pub use share::Share;
|
||||
|
|
|
|||
Loading…
Reference in a new issue