Compare commits

..

No commits in common. "357f455ec0f6137f52b4d818be39d432ac1eae89" and "d8c48b74ca247039c093f0463e2907dafcffc88e" have entirely different histories.

16 changed files with 110 additions and 142 deletions

View file

@ -6,9 +6,10 @@ use log::{debug, warn};
use crate::{ use crate::{
cachefile::CacheFile, cachefile::CacheFile,
cli::Cli, cli::Cli,
error,
file::{Chunk, FileTrait}, file::{Chunk, FileTrait},
output::new_progressbar, output::new_progressbar,
sharry::{Client, ShareID}, sharry::Client,
}; };
pub struct AppState { pub struct AppState {
@ -32,7 +33,7 @@ fn new_http(args: &Cli) -> ureq::Agent {
.into() .into()
} }
fn new_share(args: &Cli) -> crate::Result<ShareID> { fn new_share(args: &Cli) -> error::Result<String> {
new_http(args).share_create(&args.get_uri(), &args.alias, args.get_share_request()) new_http(args).share_create(&args.get_uri(), &args.alias, args.get_share_request())
} }
@ -45,11 +46,11 @@ impl AppState {
} }
} }
pub fn try_resume(args: &Cli) -> crate::Result<Self> { pub fn try_resume(args: &Cli) -> error::Result<Self> {
Ok(Self::new(new_http(args), CacheFile::try_resume(args)?)) Ok(Self::new(new_http(args), CacheFile::try_resume(args)?))
} }
pub fn from_args(args: &Cli) -> crate::Result<Self> { pub fn from_args(args: &Cli) -> error::Result<Self> {
Ok(Self::new( Ok(Self::new(
new_http(args), new_http(args),
CacheFile::from_args(args, new_share)?, CacheFile::from_args(args, new_share)?,
@ -86,7 +87,7 @@ impl AppState {
self.with_progressbar(f, true); self.with_progressbar(f, true);
} }
fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> crate::Result<Option<Chunk<'t>>> { fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> error::Result<Option<Chunk<'t>>> {
if self.inner.get_uploading(&self.http)?.is_none() { if self.inner.get_uploading(&self.http)?.is_none() {
return Ok(None); return Ok(None);
} }
@ -102,7 +103,7 @@ impl AppState {
Ok(Some(chunk)) Ok(Some(chunk))
} }
pub fn upload_chunk(&mut self, buffer: &mut [u8]) -> crate::Result<bool> { pub fn upload_chunk(&mut self, buffer: &mut [u8]) -> error::Result<bool> {
let Some(chunk) = self.next_chunk(buffer)? else { let Some(chunk) = self.next_chunk(buffer)? else {
self.inner self.inner
.share_notify(&self.http) .share_notify(&self.http)
@ -135,7 +136,7 @@ impl AppState {
self.drop_progressbar(ProgressBar::abandon); self.drop_progressbar(ProgressBar::abandon);
} }
pub fn rebuild_share(self, args: &Cli) -> crate::Result<Self> { pub fn rebuild_share(self, args: &Cli) -> error::Result<Self> {
Ok(Self::new(self.http, CacheFile::from_args(args, new_share)?)) Ok(Self::new(self.http, CacheFile::from_args(args, new_share)?))
} }

View file

@ -11,7 +11,7 @@ use console::{StyledObject, style};
use log::{info, trace}; use log::{info, trace};
use shrupl::{ use shrupl::{
AppState, Cli, AppState, Cli, error,
output::{self, Log, SHRUPL}, output::{self, Log, SHRUPL},
}; };
@ -86,16 +86,16 @@ fn main() {
Err(e) => { Err(e) => {
Log::handle(&e); Log::handle(&e);
if let shrupl::Error::InvalidParameter(p) = e { if let error::Error::InvalidParameter(p) = e {
match p { match p {
// Error 404 (File not found) // Error 404 (File not found)
shrupl::Parameter::FileID(fid) => { error::Parameter::FileID(fid) => {
info!("retrying file {fid:?}"); info!("retrying file {fid:?}");
state.abort_upload(); state.abort_upload();
} }
// Error 404 (Share not found) // Error 404 (Share not found)
shrupl::Parameter::ShareID(sid) => { error::Parameter::ShareID(sid) => {
output::prompt_rebuild_share(); output::prompt_rebuild_share();
info!("rebuilding share {sid:?}"); info!("rebuilding share {sid:?}");

View file

@ -12,9 +12,10 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
cli::Cli, cli::Cli,
error,
file::{self, Chunk, FileTrait}, file::{self, Chunk, FileTrait},
output::new_progressbar, output::new_progressbar,
sharry::{AliasID, Client, ShareID, Uri}, sharry::{Client, Uri},
}; };
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -23,8 +24,8 @@ pub struct CacheFile {
file_name: PathBuf, file_name: PathBuf,
uri: Uri, uri: Uri,
alias_id: AliasID, alias_id: String,
share_id: ShareID, share_id: String,
uploading: Option<file::Uploading>, uploading: Option<file::Uploading>,
files: VecDeque<file::Checked>, files: VecDeque<file::Checked>,
@ -47,7 +48,7 @@ impl CacheFile {
file_name file_name
} }
pub fn try_resume(args: &Cli) -> crate::Result<Self> { pub fn try_resume(args: &Cli) -> error::Result<Self> {
let file_name = Self::cache_file(args); let file_name = Self::cache_file(args);
let state: Self = { let state: Self = {
@ -60,7 +61,7 @@ impl CacheFile {
fn check_hash<'a>( fn check_hash<'a>(
file: &'a impl FileTrait<'a>, file: &'a impl FileTrait<'a>,
bar: &ProgressBar, bar: &ProgressBar,
) -> crate::Result<()> { ) -> error::Result<()> {
bar.set_message(format!("checking {:?}", file.get_name())); bar.set_message(format!("checking {:?}", file.get_name()));
file.check_hash(|bytes| bar.inc(bytes)) file.check_hash(|bytes| bar.inc(bytes))
} }
@ -97,8 +98,8 @@ impl CacheFile {
pub fn from_args( pub fn from_args(
args: &Cli, args: &Cli,
new_share: impl FnOnce(&Cli) -> crate::Result<ShareID>, new_share: impl FnOnce(&Cli) -> error::Result<String>,
) -> crate::Result<Self> { ) -> error::Result<Self> {
let mut files = args.files.clone(); let mut files = args.files.clone();
if args.should_hash() { if args.should_hash() {
@ -134,7 +135,7 @@ impl CacheFile {
pub fn get_uploading( pub fn get_uploading(
&mut self, &mut self,
client: &impl Client, client: &impl Client,
) -> crate::Result<Option<&mut file::Uploading>> { ) -> error::Result<Option<&mut file::Uploading>> {
if self.uploading.is_some() { if self.uploading.is_some() {
Ok(self.uploading.as_mut()) Ok(self.uploading.as_mut())
} else if let Some(chk) = self.files.pop_front() { } else if let Some(chk) = self.files.pop_front() {
@ -188,11 +189,11 @@ impl CacheFile {
); );
} }
pub fn share_notify(&self, client: &impl Client) -> crate::Result<()> { pub fn share_notify(&self, client: &impl Client) -> error::Result<()> {
client.share_notify(&self.uri, &self.alias_id, &self.share_id) client.share_notify(&self.uri, &self.alias_id, &self.share_id)
} }
pub fn file_patch(&self, client: &impl Client, chunk: &Chunk) -> crate::Result<()> { pub fn file_patch(&self, client: &impl Client, chunk: &Chunk) -> error::Result<()> {
client.file_patch(&self.uri, &self.alias_id, &self.share_id, chunk) client.file_patch(&self.uri, &self.alias_id, &self.share_id, chunk)
} }

View file

@ -11,7 +11,7 @@ use log::LevelFilter;
use crate::{ use crate::{
file::{Checked, FileTrait}, file::{Checked, FileTrait},
sharry::{AliasID, NewShareRequest, Uri}, sharry::{NewShareRequest, Uri},
}; };
#[derive(Parser)] #[derive(Parser)]
@ -69,7 +69,7 @@ pub struct Cli {
url: String, url: String,
/// ID of a public alias to use /// ID of a public alias to use
pub alias: AliasID, pub alias: String,
/// Files to upload to the new share /// Files to upload to the new share
#[arg(value_name = "FILE", required = true, value_parser = parse_sharry_file)] #[arg(value_name = "FILE", required = true, value_parser = parse_sharry_file)]
@ -159,7 +159,7 @@ impl Cli {
let mut hasher = Blake2b::new().hash_length(16).to_state(); let mut hasher = Blake2b::new().hash_length(16).to_state();
hasher.update(self.get_uri().as_ref()); hasher.update(self.get_uri().as_ref());
hasher.update(self.alias.as_ref()); hasher.update(self.alias.as_bytes());
for chk in sorted(&self.files) { for chk in sorted(&self.files) {
hasher.update(chk.as_ref()); hasher.update(chk.as_ref());

View file

@ -8,10 +8,10 @@ pub enum Parameter {
Uri(String), Uri(String),
#[error("given Alias ID {0:?}")] #[error("given Alias ID {0:?}")]
AliasID(sharry::AliasID), AliasID(String),
#[error("stored Share ID {0:?}")] #[error("stored Share ID {0:?}")]
ShareID(sharry::ShareID), ShareID(String),
#[error("stored {0:?}")] #[error("stored {0:?}")]
FileID(sharry::FileID), FileID(sharry::FileID),
@ -75,7 +75,8 @@ impl Error {
pub fn is_fatal(&self) -> bool { pub fn is_fatal(&self) -> bool {
match self { match self {
Self::InvalidParameter(p) => p.is_fatal(), Self::InvalidParameter(p) => p.is_fatal(),
Self::Mismatch { .. } | Self::Unknown(_) => true, Self::Mismatch { .. } => true,
Self::Unknown(_) => true,
_ => false, _ => false,
} }
} }

View file

@ -5,7 +5,7 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::sharry; use crate::{error, sharry};
use super::{FileTrait, Uploading}; use super::{FileTrait, Uploading};
@ -53,9 +53,9 @@ impl Checked {
} }
} }
pub fn hash(&mut self, f: impl Fn(u64)) -> crate::Result<()> { pub fn hash(&mut self, f: impl Fn(u64)) -> error::Result<()> {
if self.hash.is_some() { if self.hash.is_some() {
return Err(crate::Error::mismatch("unhashed file", self.path.display())); return Err(error::Error::mismatch("unhashed file", self.path.display()));
} }
self.hash = Some(super::compute_file_hash(&self.path, self.size, f)?); self.hash = Some(super::compute_file_hash(&self.path, self.size, f)?);
@ -76,9 +76,9 @@ impl Checked {
self, self,
client: &impl sharry::Client, client: &impl sharry::Client,
uri: &sharry::Uri, uri: &sharry::Uri,
alias_id: &sharry::AliasID, alias_id: &str,
share_id: &sharry::ShareID, share_id: &str,
) -> crate::Result<Uploading> { ) -> error::Result<Uploading> {
let file_id = client.file_create(uri, alias_id, share_id, &self)?; let file_id = client.file_create(uri, alias_id, share_id, &self)?;
Ok(Uploading::new(self.path, self.size, self.hash, file_id)) Ok(Uploading::new(self.path, self.size, self.hash, file_id))
@ -98,7 +98,7 @@ impl<'t> FileTrait<'t> for Checked {
self.size self.size
} }
fn check_hash(&self, on_progress: impl Fn(u64)) -> crate::Result<()> { fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()> {
super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress) super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress)
} }
} }

View file

@ -12,8 +12,9 @@ pub use chunk::Chunk;
use log::{debug, warn}; use log::{debug, warn};
pub use uploading::Uploading; pub use uploading::Uploading;
use crate::error;
fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> crate::Result<String> { fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> error::Result<String> {
let mut file = fs::File::open(path)?; let mut file = fs::File::open(path)?;
let mut hasher = Blake2b::new().hash_length(64).to_state(); let mut hasher = Blake2b::new().hash_length(64).to_state();
@ -32,7 +33,7 @@ fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> crate
} }
if bytes_read != size { if bytes_read != size {
return Err(crate::Error::mismatch(size, bytes_read)); return Err(error::Error::mismatch(size, bytes_read));
} }
let result = Base64::encode_string(hasher.finalize().as_bytes()); let result = Base64::encode_string(hasher.finalize().as_bytes());
@ -45,9 +46,9 @@ fn check_file_hash(
size: u64, size: u64,
hash: Option<&String>, hash: Option<&String>,
on_progress: impl Fn(u64), on_progress: impl Fn(u64),
) -> crate::Result<()> { ) -> error::Result<()> {
let Some(expected) = hash else { let Some(expected) = hash else {
return Err(crate::Error::mismatch("hash", path.display())); return Err(error::Error::mismatch("hash", path.display()));
}; };
let actual = &compute_file_hash(path, size, on_progress)?; let actual = &compute_file_hash(path, size, on_progress)?;
@ -57,7 +58,7 @@ fn check_file_hash(
Ok(()) Ok(())
} else { } else {
warn!("hash mismatch for file {:?}", path.display()); warn!("hash mismatch for file {:?}", path.display());
Err(crate::Error::mismatch(expected, actual)) Err(error::Error::mismatch(expected, actual))
} }
} }
@ -79,5 +80,5 @@ pub trait FileTrait<'t> {
/// get the file's size /// get the file's size
fn get_size(&self) -> u64; fn get_size(&self) -> u64;
fn check_hash(&self, on_progress: impl Fn(u64)) -> crate::Result<()>; fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()>;
} }

View file

@ -7,7 +7,7 @@ use std::{
use log::warn; use log::warn;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::sharry; use crate::{error, sharry};
use super::{Checked, Chunk, FileTrait}; use super::{Checked, Chunk, FileTrait};
@ -108,7 +108,7 @@ impl<'t> FileTrait<'t> for Uploading {
self.size self.size
} }
fn check_hash(&self, on_progress: impl Fn(u64)) -> crate::Result<()> { fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()> {
super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress) super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress)
} }
} }

View file

@ -1,31 +1,32 @@
use log::{debug, trace}; use log::{debug, trace};
use crate::{ use crate::{
error,
file::{self, FileTrait}, file::{self, FileTrait},
sharry::{self, AliasID, FileID, ShareID, Uri}, sharry::{self, FileID, Uri},
}; };
fn find_cause( fn find_cause(
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
share_id: Option<&ShareID>, share_id: Option<&str>,
file_id: Option<&FileID>, file_id: Option<&FileID>,
) -> impl FnOnce(ureq::Error) -> crate::Error { ) -> impl FnOnce(ureq::Error) -> error::Error {
move |error| match error { move |error| match error {
ureq::Error::StatusCode(403) => { ureq::Error::StatusCode(403) => {
trace!("HTTP Error 403: Alias not found!"); trace!("HTTP Error 403: Alias not found!");
crate::Error::InvalidParameter(crate::Parameter::AliasID(alias_id.to_owned())) error::Error::InvalidParameter(error::Parameter::AliasID(alias_id.to_owned()))
} }
ureq::Error::StatusCode(404) => { ureq::Error::StatusCode(404) => {
trace!("HTTP Error 404: Share and/or file may have been deleted!"); trace!("HTTP Error 404: Share and/or file may have been deleted!");
if let Some(file_id) = file_id { if let Some(file_id) = file_id {
crate::Error::InvalidParameter(crate::Parameter::FileID(file_id.to_owned())) error::Error::InvalidParameter(error::Parameter::FileID(file_id.clone()))
} else if let Some(share_id) = share_id { } else if let Some(share_id) = share_id {
crate::Error::InvalidParameter(crate::Parameter::ShareID(share_id.to_owned())) error::Error::InvalidParameter(error::Parameter::ShareID(share_id.to_owned()))
} else { } else {
crate::Error::InvalidParameter(crate::Parameter::Uri(uri.to_string())) error::Error::InvalidParameter(error::Parameter::Uri(uri.to_string()))
} }
} }
ureq::Error::Io(error) => { ureq::Error::Io(error) => {
@ -33,7 +34,7 @@ fn find_cause(
if let Some(msg) = error.get_ref().map(ToString::to_string) { if let Some(msg) = error.get_ref().map(ToString::to_string) {
if msg.starts_with("failed to lookup address information") { if msg.starts_with("failed to lookup address information") {
crate::Error::InvalidParameter(crate::Parameter::Uri(uri.to_string())) error::Error::InvalidParameter(error::Parameter::Uri(uri.to_string()))
} else { } else {
error.into() error.into()
} }
@ -41,7 +42,7 @@ fn find_cause(
error.into() error.into()
} }
} }
error => crate::Error::Unknown(error.to_string()), error => error::Error::Unknown(error.to_string()),
} }
} }
@ -49,24 +50,24 @@ impl sharry::Client for ureq::Agent {
fn share_create( fn share_create(
&self, &self,
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
data: sharry::NewShareRequest, data: sharry::NewShareRequest,
) -> crate::Result<ShareID> { ) -> error::Result<String> {
let res = { let res = {
let endpoint = uri.share_create(); let endpoint = uri.share_create();
let mut res = self let mut res = self
.post(&endpoint) .post(&endpoint)
.header("Sharry-Alias", alias_id.as_ref()) .header("Sharry-Alias", alias_id)
.send_json(data) .send_json(data)
.map_err(find_cause(uri, alias_id, None, None))?; .map_err(find_cause(uri, alias_id, None, None))?;
trace!("{endpoint:?} response: {res:?}"); trace!("{endpoint:?} response: {res:?}");
crate::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?; error::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?;
res.body_mut() res.body_mut()
.read_json::<sharry::NewShareResponse>() .read_json::<sharry::NewShareResponse>()
.map_err(crate::Error::response)? .map_err(error::Error::response)?
}; };
debug!("{res:?}"); debug!("{res:?}");
@ -74,28 +75,28 @@ impl sharry::Client for ureq::Agent {
if res.success && (res.message == "Share created.") { if res.success && (res.message == "Share created.") {
trace!("new share id: {:?}", res.id); trace!("new share id: {:?}", res.id);
Ok(res.id.into()) Ok(res.id)
} else { } else {
Err(crate::Error::response(format!("{res:?}"))) Err(error::Error::response(format!("{res:?}")))
} }
} }
fn share_notify(&self, uri: &Uri, alias_id: &AliasID, share_id: &ShareID) -> crate::Result<()> { fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> error::Result<()> {
let res = { let res = {
let endpoint = uri.share_notify(share_id); let endpoint = uri.share_notify(share_id);
let mut res = self let mut res = self
.post(&endpoint) .post(&endpoint)
.header("Sharry-Alias", alias_id.as_ref()) .header("Sharry-Alias", alias_id)
.send_empty() .send_empty()
.map_err(find_cause(uri, alias_id, Some(share_id), None))?; .map_err(find_cause(uri, alias_id, Some(share_id), None))?;
trace!("{endpoint:?} response: {res:?}"); trace!("{endpoint:?} response: {res:?}");
crate::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?; error::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?;
res.body_mut() res.body_mut()
.read_json::<sharry::NotifyShareResponse>() .read_json::<sharry::NotifyShareResponse>()
.map_err(crate::Error::response)? .map_err(error::Error::response)?
}; };
debug!("{res:?}"); debug!("{res:?}");
@ -106,30 +107,30 @@ impl sharry::Client for ureq::Agent {
fn file_create( fn file_create(
&self, &self,
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
share_id: &ShareID, share_id: &str,
file: &file::Checked, file: &file::Checked,
) -> crate::Result<FileID> { ) -> error::Result<FileID> {
let res = { let res = {
let endpoint = uri.file_create(share_id); let endpoint = uri.file_create(share_id);
let res = self let res = self
.post(&endpoint) .post(&endpoint)
.header("Sharry-Alias", alias_id.as_ref()) .header("Sharry-Alias", alias_id)
.header("Sharry-File-Name", file.get_name()) .header("Sharry-File-Name", file.get_name())
.header("Upload-Length", file.get_size()) .header("Upload-Length", file.get_size())
.send_empty() .send_empty()
.map_err(find_cause(uri, alias_id, Some(share_id), None))?; .map_err(find_cause(uri, alias_id, Some(share_id), None))?;
trace!("{endpoint:?} response: {res:?}"); trace!("{endpoint:?} response: {res:?}");
crate::Error::res_status_check(res.status(), ureq::http::StatusCode::CREATED)?; error::Error::res_status_check(res.status(), ureq::http::StatusCode::CREATED)?;
res res
}; };
let location = (res.headers().get("Location")) let location = (res.headers().get("Location"))
.ok_or_else(|| crate::Error::response("Location header not found"))? .ok_or_else(|| error::Error::response("Location header not found"))?
.to_str() .to_str()
.map_err(crate::Error::response)? .map_err(error::Error::response)?
.to_string(); .to_string();
FileID::try_from(location) FileID::try_from(location)
@ -138,16 +139,16 @@ impl sharry::Client for ureq::Agent {
fn file_patch( fn file_patch(
&self, &self,
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
share_id: &ShareID, share_id: &str,
chunk: &file::Chunk, chunk: &file::Chunk,
) -> crate::Result<()> { ) -> error::Result<()> {
let res = { let res = {
let endpoint = uri.file_patch(share_id, chunk.get_file_id()); let endpoint = uri.file_patch(share_id, chunk.get_file_id());
let res = self let res = self
.patch(&endpoint) .patch(&endpoint)
.header("Sharry-Alias", alias_id.as_ref()) .header("Sharry-Alias", alias_id)
.header("Upload-Offset", chunk.get_offset()) .header("Upload-Offset", chunk.get_offset())
.send(chunk.get_data()) .send(chunk.get_data())
.map_err(find_cause( .map_err(find_cause(
@ -158,21 +159,21 @@ impl sharry::Client for ureq::Agent {
))?; ))?;
trace!("{endpoint:?} response: {res:?}"); trace!("{endpoint:?} response: {res:?}");
crate::Error::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?; error::Error::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?;
res res
}; };
let res_offset = (res.headers().get("Upload-Offset")) let res_offset = (res.headers().get("Upload-Offset"))
.ok_or_else(|| crate::Error::response("Upload-Offset header not found"))? .ok_or_else(|| error::Error::response("Upload-Offset header not found"))?
.to_str() .to_str()
.map_err(crate::Error::response)? .map_err(error::Error::response)?
.parse::<u64>() .parse::<u64>()
.map_err(crate::Error::response)?; .map_err(error::Error::response)?;
if chunk.get_behind() == res_offset { if chunk.get_behind() == res_offset {
Ok(()) Ok(())
} else { } else {
Err(crate::Error::response(format!( Err(error::Error::response(format!(
"Unexpected Upload-Offset: {} (expected {})", "Unexpected Upload-Offset: {} (expected {})",
res_offset, res_offset,
chunk.get_behind() chunk.get_behind()

View file

@ -4,7 +4,7 @@
mod appstate; mod appstate;
mod cachefile; mod cachefile;
mod cli; mod cli;
mod error; pub mod error;
mod file; mod file;
mod impl_ureq; mod impl_ureq;
pub mod output; pub mod output;
@ -12,4 +12,3 @@ mod sharry;
pub use appstate::AppState; pub use appstate::AppState;
pub use cli::Cli; pub use cli::Cli;
pub use error::{Error, Parameter, Result};

View file

@ -48,7 +48,7 @@ pub fn prompt_rebuild_share() {
.interact() .interact()
.unwrap_or(false); .unwrap_or(false);
if !selection { if selection == false {
process::exit(0); process::exit(0);
} }
} }
@ -92,7 +92,7 @@ impl Log {
process::exit(1); process::exit(1);
} }
pub fn handle(e: &crate::Error) { pub fn handle(e: &crate::error::Error) {
if e.is_fatal() { if e.is_fatal() {
// react to fatal error // react to fatal error
warn!("fatal error: {e:?}"); warn!("fatal error: {e:?}");

View file

@ -4,41 +4,10 @@ use log::{debug, trace};
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)] use crate::error;
pub struct AliasID(String);
impl fmt::Display for AliasID { // pub struct AliasID(String);
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // pub struct ShareID(String);
f.write_str(&self.0)
}
}
impl AsRef<[u8]> for AliasID {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl From<String> for AliasID {
fn from(value: String) -> Self {
Self(value)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ShareID(String);
impl fmt::Display for ShareID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl From<String> for ShareID {
fn from(value: String) -> Self {
Self(value)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FileID(String); pub struct FileID(String);
@ -50,9 +19,9 @@ impl fmt::Display for FileID {
} }
impl TryFrom<String> for FileID { impl TryFrom<String> for FileID {
type Error = crate::Error; type Error = error::Error;
fn try_from(value: String) -> crate::Result<Self> { fn try_from(value: String) -> error::Result<Self> {
/// Pattern breakdown: /// Pattern breakdown:
/// - `^([^:/?#]+)://` scheme (anything but `:/?#`) + `"://"` /// - `^([^:/?#]+)://` scheme (anything but `:/?#`) + `"://"`
/// - `([^/?#]+)` authority/host (anything but `/?#`) /// - `([^/?#]+)` authority/host (anything but `/?#`)
@ -81,7 +50,7 @@ impl TryFrom<String> for FileID {
Ok(result) Ok(result)
} else { } else {
Err(crate::Error::mismatch( Err(error::Error::mismatch(
"<proto>://<base>/api/v2/alias/upload/<share>/files/tus/<file>", "<proto>://<base>/api/v2/alias/upload/<share>/files/tus/<file>",
value, value,
)) ))
@ -141,7 +110,7 @@ mod tests {
let err = FileID::try_from(bad.to_string()).expect_err("URL should not parse"); let err = FileID::try_from(bad.to_string()).expect_err("URL should not parse");
// make sure it's the Mismatch variant, and that it contains the original input // make sure it's the Mismatch variant, and that it contains the original input
match err { match err {
crate::Error::Mismatch { expected, actual } => { error::Error::Mismatch { expected, actual } => {
assert_eq!( assert_eq!(
expected, "<proto>://<base>/api/v2/alias/upload/<share>/files/tus/<file>", expected, "<proto>://<base>/api/v2/alias/upload/<share>/files/tus/<file>",
"Error should output expected format" "Error should output expected format"

View file

@ -2,6 +2,6 @@ mod id;
mod json; mod json;
mod uri; mod uri;
pub use id::{AliasID, FileID, ShareID}; pub use id::FileID;
pub use json::{NewShareRequest, NewShareResponse, NotifyShareResponse}; pub use json::{NewShareRequest, NewShareResponse, NotifyShareResponse};
pub use uri::Uri; pub use uri::Uri;

View file

@ -33,15 +33,15 @@ impl Uri {
self.endpoint(format_args!("alias/upload/new")) self.endpoint(format_args!("alias/upload/new"))
} }
pub fn share_notify(&self, share_id: &super::ShareID) -> String { pub fn share_notify(&self, share_id: &str) -> String {
self.endpoint(format_args!("alias/mail/notify/{share_id}")) self.endpoint(format_args!("alias/mail/notify/{share_id}"))
} }
pub fn file_create(&self, share_id: &super::ShareID) -> String { pub fn file_create(&self, share_id: &str) -> String {
self.endpoint(format_args!("alias/upload/{share_id}/files/tus")) self.endpoint(format_args!("alias/upload/{share_id}/files/tus"))
} }
pub fn file_patch(&self, share_id: &super::ShareID, file_id: &super::FileID) -> String { pub fn file_patch(&self, share_id: &str, file_id: &super::FileID) -> String {
self.endpoint(format_args!("alias/upload/{share_id}/files/tus/{file_id}")) self.endpoint(format_args!("alias/upload/{share_id}/files/tus/{file_id}"))
} }
} }

View file

@ -1,33 +1,30 @@
use crate::file; use crate::{error, file};
use super::{ use super::api::{FileID, NewShareRequest, Uri};
AliasID, ShareID,
api::{FileID, NewShareRequest, Uri},
};
pub trait Client { pub trait Client {
fn share_create( fn share_create(
&self, &self,
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
data: NewShareRequest, data: NewShareRequest,
) -> crate::Result<ShareID>; ) -> error::Result<String>;
fn share_notify(&self, uri: &Uri, alias_id: &AliasID, share_id: &ShareID) -> crate::Result<()>; fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> error::Result<()>;
fn file_create( fn file_create(
&self, &self,
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
share_id: &ShareID, share_id: &str,
file: &file::Checked, file: &file::Checked,
) -> crate::Result<FileID>; ) -> error::Result<FileID>;
fn file_patch( fn file_patch(
&self, &self,
uri: &Uri, uri: &Uri,
alias_id: &AliasID, alias_id: &str,
share_id: &ShareID, share_id: &str,
chunk: &file::Chunk, chunk: &file::Chunk,
) -> crate::Result<()>; ) -> error::Result<()>;
} }

View file

@ -1,7 +1,5 @@
mod api; mod api;
mod client; mod client;
pub use api::{ pub use api::{FileID, NewShareRequest, NewShareResponse, NotifyShareResponse, Uri};
AliasID, FileID, NewShareRequest, NewShareResponse, NotifyShareResponse, ShareID, Uri,
};
pub use client::Client; pub use client::Client;