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