create struct sharry::api::FileID
This commit is contained in:
parent
f2b063ba85
commit
63057b805c
7 changed files with 110 additions and 78 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::sharry;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Parameter {
|
pub enum Parameter {
|
||||||
#[error("given URI {0:?}")]
|
#[error("given URI {0:?}")]
|
||||||
|
|
@ -11,8 +13,8 @@ pub enum Parameter {
|
||||||
#[error("stored Share ID {0:?}")]
|
#[error("stored Share ID {0:?}")]
|
||||||
ShareID(String),
|
ShareID(String),
|
||||||
|
|
||||||
#[error("stored File ID {0:?}")]
|
#[error("stored {0:?}")]
|
||||||
FileID(String),
|
FileID(sharry::FileID),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::sharry;
|
||||||
|
|
||||||
pub struct Chunk<'t> {
|
pub struct Chunk<'t> {
|
||||||
file_id: String,
|
file_id: sharry::FileID,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
data: &'t [u8],
|
data: &'t [u8],
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +19,7 @@ impl fmt::Debug for Chunk<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t> Chunk<'t> {
|
impl<'t> Chunk<'t> {
|
||||||
pub fn new(file_id: String, offset: u64, data: &'t [u8]) -> Self {
|
pub fn new(file_id: sharry::FileID, offset: u64, data: &'t [u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_id,
|
file_id,
|
||||||
offset,
|
offset,
|
||||||
|
|
@ -25,7 +27,7 @@ impl<'t> Chunk<'t> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_file_id(&self) -> &str {
|
pub fn get_file_id(&self) -> &sharry::FileID {
|
||||||
&self.file_id
|
&self.file_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ use std::{
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::sharry;
|
||||||
|
|
||||||
use super::{Checked, Chunk, FileTrait};
|
use super::{Checked, Chunk, FileTrait};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
@ -17,14 +19,19 @@ pub struct Uploading {
|
||||||
size: u64,
|
size: u64,
|
||||||
/// hash of that file
|
/// hash of that file
|
||||||
hash: Option<String>,
|
hash: Option<String>,
|
||||||
file_id: String,
|
file_id: sharry::FileID,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
last_offset: Option<u64>,
|
last_offset: Option<u64>,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Uploading {
|
impl Uploading {
|
||||||
pub(super) fn new(path: PathBuf, size: u64, hash: Option<String>, file_id: String) -> Self {
|
pub(super) fn new(
|
||||||
|
path: PathBuf,
|
||||||
|
size: u64,
|
||||||
|
hash: Option<String>,
|
||||||
|
file_id: sharry::FileID,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
path,
|
path,
|
||||||
size,
|
size,
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ use log::{debug, trace};
|
||||||
use crate::{
|
use crate::{
|
||||||
error,
|
error,
|
||||||
file::{self, FileTrait},
|
file::{self, FileTrait},
|
||||||
sharry::{self, Uri},
|
sharry::{self, FileID, Uri},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn find_cause(
|
fn find_cause(
|
||||||
uri: &Uri,
|
uri: &Uri,
|
||||||
alias_id: &str,
|
alias_id: &str,
|
||||||
share_id: Option<&str>,
|
share_id: Option<&str>,
|
||||||
file_id: Option<&str>,
|
file_id: Option<&FileID>,
|
||||||
) -> impl FnOnce(ureq::Error) -> error::Error {
|
) -> impl FnOnce(ureq::Error) -> error::Error {
|
||||||
move |error| match error {
|
move |error| match error {
|
||||||
ureq::Error::StatusCode(403) => {
|
ureq::Error::StatusCode(403) => {
|
||||||
|
|
@ -22,7 +22,7 @@ fn find_cause(
|
||||||
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 {
|
||||||
error::Error::InvalidParameter(error::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 {
|
||||||
error::Error::InvalidParameter(error::Parameter::ShareID(share_id.to_owned()))
|
error::Error::InvalidParameter(error::Parameter::ShareID(share_id.to_owned()))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -110,7 +110,7 @@ impl sharry::Client for ureq::Agent {
|
||||||
alias_id: &str,
|
alias_id: &str,
|
||||||
share_id: &str,
|
share_id: &str,
|
||||||
file: &file::Checked,
|
file: &file::Checked,
|
||||||
) -> error::Result<String> {
|
) -> error::Result<FileID> {
|
||||||
let res = {
|
let res = {
|
||||||
let endpoint = uri.file_create(share_id);
|
let endpoint = uri.file_create(share_id);
|
||||||
|
|
||||||
|
|
@ -133,11 +133,7 @@ impl sharry::Client for ureq::Agent {
|
||||||
.map_err(error::Error::response)?
|
.map_err(error::Error::response)?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let file_id = Self::get_file_id(&location)?;
|
FileID::try_from(location)
|
||||||
|
|
||||||
debug!("location: {location:?}, file_id: {file_id:?}");
|
|
||||||
|
|
||||||
Ok(file_id.to_owned())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_patch(
|
fn file_patch(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use std::fmt;
|
use std::{fmt, sync::LazyLock};
|
||||||
|
|
||||||
use log::trace;
|
use log::{debug, trace};
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Uri(String);
|
pub struct Uri(String);
|
||||||
|
|
||||||
|
|
@ -41,11 +44,89 @@ impl Uri {
|
||||||
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: &str, file_id: &str) -> String {
|
pub fn file_patch(&self, share_id: &str, file_id: &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}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub struct AliasID(String);
|
||||||
|
// pub struct ShareID(String);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct FileID(String);
|
||||||
|
|
||||||
|
impl fmt::Display for FileID {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for FileID {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("File ID").field(&self.0).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for FileID {
|
||||||
|
type Error = error::Error;
|
||||||
|
|
||||||
|
fn try_from(value: String) -> error::Result<Self> {
|
||||||
|
/// Pattern breakdown:
|
||||||
|
/// - `^([^:/?#]+)://` – scheme (anything but `:/?#`) + `"://"`
|
||||||
|
/// - `([^/?#]+)` – authority/host (anything but `/?#`)
|
||||||
|
/// - `/api/v2/alias/upload/` – literal path segment
|
||||||
|
/// - `([^/]+)` – capture SID (one or more non-slash chars)
|
||||||
|
/// - `/files/tus/` – literal path segment
|
||||||
|
/// - `(?P<fid>[^/]+)` – capture FID (one or more non-slash chars)
|
||||||
|
/// - `$` – end of string
|
||||||
|
static UPLOAD_URL_RE: LazyLock<Regex> = LazyLock::new(|| {
|
||||||
|
trace!("compiling UPLOAD_URL_RE");
|
||||||
|
|
||||||
|
Regex::new(
|
||||||
|
r"^([^:/?#]+)://([^/?#]+)/api/v2/alias/upload/[^/]+/files/tus/(?P<fid>[^/]+)$",
|
||||||
|
)
|
||||||
|
.expect("Regex compilation failed")
|
||||||
|
});
|
||||||
|
|
||||||
|
trace!("TryFrom {value:?}");
|
||||||
|
|
||||||
|
if let Some(fid) = UPLOAD_URL_RE
|
||||||
|
.captures(&value)
|
||||||
|
.and_then(|caps| caps.name("fid").map(|m| m.as_str()))
|
||||||
|
{
|
||||||
|
let result = Self(fid.to_owned());
|
||||||
|
debug!("{result:?}");
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
Err(error::Error::unknown(format!(
|
||||||
|
"Could not extract File ID from {value:?}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move into tests subdir
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use super::*;
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn test_get_file_id() {
|
||||||
|
// let good = "https://example.com/api/v2/alias/upload/SID123/files/tus/FID456";
|
||||||
|
// let good = Client::get_file_id(good);
|
||||||
|
// assert!(good.is_ok());
|
||||||
|
// assert_eq!(good.unwrap(), "FID456");
|
||||||
|
|
||||||
|
// let bad = "https://example.com/api/v2/alias/upload//files/tus/FID456"; // missing SID
|
||||||
|
// assert!(Client::get_file_id(bad).is_err());
|
||||||
|
|
||||||
|
// let bad: &'static str = "https://example.com/api/v2/alias/upload/SID123/files/tus/"; // missing FID
|
||||||
|
// assert!(Client::get_file_id(bad).is_err());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub struct NewShareRequest {
|
pub struct NewShareRequest {
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,8 @@
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
use log::trace;
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
use crate::{error, file};
|
use crate::{error, file};
|
||||||
|
|
||||||
use super::api::{NewShareRequest, Uri};
|
use super::api::{FileID, NewShareRequest, Uri};
|
||||||
|
|
||||||
pub trait Client {
|
pub trait Client {
|
||||||
fn get_file_id(uri: &str) -> error::Result<&str> {
|
|
||||||
/// Pattern breakdown:
|
|
||||||
/// - `^([^:/?#]+)://` – scheme (anything but `:/?#`) + `"://"`
|
|
||||||
/// - `([^/?#]+)` – authority/host (anything but `/?#`)
|
|
||||||
/// - `/api/v2/alias/upload/` – literal path segment
|
|
||||||
/// - `([^/]+)` – capture SID (one or more non-slash chars)
|
|
||||||
/// - `/files/tus/` – literal path segment
|
|
||||||
/// - `(?P<fid>[^/]+)` – capture FID (one or more non-slash chars)
|
|
||||||
/// - `$` – end of string
|
|
||||||
static UPLOAD_URL_RE: LazyLock<Regex> = LazyLock::new(|| {
|
|
||||||
trace!("compiling UPLOAD_URL_RE");
|
|
||||||
|
|
||||||
Regex::new(
|
|
||||||
r"^([^:/?#]+)://([^/?#]+)/api/v2/alias/upload/[^/]+/files/tus/(?P<fid>[^/]+)$",
|
|
||||||
)
|
|
||||||
.expect("Regex compilation failed")
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(fid) = UPLOAD_URL_RE
|
|
||||||
.captures(uri)
|
|
||||||
.and_then(|caps| caps.name("fid").map(|m| m.as_str()))
|
|
||||||
{
|
|
||||||
Ok(fid)
|
|
||||||
} else {
|
|
||||||
Err(error::Error::unknown(format!(
|
|
||||||
"Could not extract File ID from {uri:?}"
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn share_create(
|
fn share_create(
|
||||||
&self,
|
&self,
|
||||||
uri: &Uri,
|
uri: &Uri,
|
||||||
|
|
@ -53,7 +18,7 @@ pub trait Client {
|
||||||
alias_id: &str,
|
alias_id: &str,
|
||||||
share_id: &str,
|
share_id: &str,
|
||||||
file: &file::Checked,
|
file: &file::Checked,
|
||||||
) -> error::Result<String>;
|
) -> error::Result<FileID>;
|
||||||
|
|
||||||
fn file_patch(
|
fn file_patch(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -63,24 +28,3 @@ pub trait Client {
|
||||||
chunk: &file::Chunk,
|
chunk: &file::Chunk,
|
||||||
) -> error::Result<()>;
|
) -> error::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move into tests subdir
|
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod tests {
|
|
||||||
// use super::*;
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_get_file_id() {
|
|
||||||
// let good = "https://example.com/api/v2/alias/upload/SID123/files/tus/FID456";
|
|
||||||
// let good = Client::get_file_id(good);
|
|
||||||
// assert!(good.is_ok());
|
|
||||||
// assert_eq!(good.unwrap(), "FID456");
|
|
||||||
|
|
||||||
// let bad = "https://example.com/api/v2/alias/upload//files/tus/FID456"; // missing SID
|
|
||||||
// assert!(Client::get_file_id(bad).is_err());
|
|
||||||
|
|
||||||
// let bad: &'static str = "https://example.com/api/v2/alias/upload/SID123/files/tus/"; // missing FID
|
|
||||||
// assert!(Client::get_file_id(bad).is_err());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mod api;
|
mod api;
|
||||||
mod client;
|
mod client;
|
||||||
|
|
||||||
pub use api::{NewShareRequest, NewShareResponse, NotifyShareResponse, Uri};
|
pub use api::{FileID, NewShareRequest, NewShareResponse, NotifyShareResponse, Uri};
|
||||||
pub use client::Client;
|
pub use client::Client;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue