use url::Url;
use std::{
borrow::Cow,
convert::Infallible,
fmt::{self, Debug, Display},
ops::{Add, AddAssign},
path::Path,
str::FromStr,
};
use crate::{BucketName, EndPoint};
#[cfg(test)]
mod test;
#[cfg(feature = "core")]
pub mod base;
#[cfg(feature = "core")]
pub use base::{InvalidObjectBase, ObjectBase};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObjectPathInner<'a>(Cow<'a, str>);
pub type ObjectPath = ObjectPathInner<'static>;
impl AsRef<str> for ObjectPathInner<'_> {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for ObjectPathInner<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Default for ObjectPathInner<'_> {
fn default() -> Self {
Self(Cow::Borrowed(""))
}
}
impl PartialEq<&str> for ObjectPathInner<'_> {
#[inline]
fn eq(&self, other: &&str) -> bool {
&self.0 == other
}
}
impl PartialEq<ObjectPathInner<'_>> for &str {
#[inline]
fn eq(&self, other: &ObjectPathInner) -> bool {
self == &other.0
}
}
impl PartialEq<String> for ObjectPathInner<'_> {
#[inline]
fn eq(&self, other: &String) -> bool {
&self.0 == other
}
}
impl PartialEq<ObjectPathInner<'_>> for String {
#[inline]
fn eq(&self, other: &ObjectPathInner) -> bool {
self == &other.0
}
}
impl<'a> ObjectPathInner<'a> {
pub fn new(val: impl Into<Cow<'a, str>>) -> Result<Self, InvalidObjectPath> {
let val = val.into();
if val.starts_with('/') || val.starts_with('.') || val.ends_with('/') {
return Err(InvalidObjectPath::new());
}
if !val.chars().all(|c| c != '\\') {
return Err(InvalidObjectPath::new());
}
Ok(Self(val))
}
pub const unsafe fn from_static(secret: &'static str) -> Self {
Self(Cow::Borrowed(secret))
}
}
impl TryFrom<String> for ObjectPathInner<'_> {
type Error = InvalidObjectPath;
fn try_from(val: String) -> Result<Self, Self::Error> {
Self::new(val)
}
}
impl TryFrom<&String> for ObjectPathInner<'_> {
type Error = InvalidObjectPath;
fn try_from(val: &String) -> Result<Self, Self::Error> {
Self::new(val.to_owned())
}
}
impl TryFrom<Box<String>> for ObjectPathInner<'_> {
type Error = InvalidObjectPath;
fn try_from(val: Box<String>) -> Result<Self, Self::Error> {
Self::new(*val)
}
}
impl<'a: 'b, 'b> TryFrom<&'a str> for ObjectPathInner<'b> {
type Error = InvalidObjectPath;
fn try_from(val: &'a str) -> Result<Self, Self::Error> {
Self::new(val)
}
}
impl FromStr for ObjectPathInner<'_> {
type Err = InvalidObjectPath;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with('/') || s.starts_with('.') || s.ends_with('/') {
return Err(InvalidObjectPath::new());
}
if !s.chars().all(|c| c != '\\') {
return Err(InvalidObjectPath::new());
}
Ok(Self(Cow::Owned(s.to_owned())))
}
}
impl TryFrom<&[u8]> for ObjectPathInner<'_> {
type Error = InvalidObjectPath;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
use std::str;
str::from_utf8(value)
.map_err(|_| InvalidObjectPath::new())
.and_then(str::parse)
}
}
impl TryFrom<&Path> for ObjectPathInner<'_> {
type Error = InvalidObjectPath;
fn try_from(value: &Path) -> Result<Self, Self::Error> {
let val = value.to_str().ok_or(InvalidObjectPath::new())?;
if std::path::MAIN_SEPARATOR != '/' {
val.replace(std::path::MAIN_SEPARATOR, "/").parse()
} else {
val.parse()
}
}
}
pub struct InvalidObjectPath {
_priv: (),
}
impl InvalidObjectPath {
pub(crate) fn new() -> Self {
Self { _priv: () }
}
}
impl Display for InvalidObjectPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid object path")
}
}
impl Debug for InvalidObjectPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InvalidObjectPath").finish()
}
}
impl std::error::Error for InvalidObjectPath {}
impl From<Infallible> for InvalidObjectPath {
fn from(_: Infallible) -> Self {
Self { _priv: () }
}
}
pub trait SetObjectPath: private::Sealed {
fn set_object_path(&mut self, path: &ObjectPathInner);
}
mod private {
pub trait Sealed {}
}
impl private::Sealed for Url {}
impl SetObjectPath for Url {
fn set_object_path(&mut self, path: &ObjectPathInner) {
self.set_path(path.as_ref());
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObjectDir<'a>(Cow<'a, str>);
impl AsRef<str> for ObjectDir<'_> {
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsMut<String> for ObjectDir<'_> {
fn as_mut(&mut self) -> &mut String {
self.0.to_mut()
}
}
impl fmt::Display for ObjectDir<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl PartialEq<&str> for ObjectDir<'_> {
#[inline]
fn eq(&self, other: &&str) -> bool {
&self.0 == other
}
}
impl PartialEq<ObjectDir<'_>> for &str {
#[inline]
fn eq(&self, other: &ObjectDir<'_>) -> bool {
self == &other.0
}
}
impl PartialEq<String> for ObjectDir<'_> {
#[inline]
fn eq(&self, other: &String) -> bool {
&self.0 == other
}
}
impl PartialEq<ObjectDir<'_>> for String {
#[inline]
fn eq(&self, other: &ObjectDir) -> bool {
self == &other.0
}
}
impl<'dir1, 'dir2: 'dir1> Add<ObjectDir<'dir2>> for ObjectDir<'dir1> {
type Output = ObjectDir<'dir1>;
fn add(self, rhs: ObjectDir<'dir2>) -> Self::Output {
let mut string = self.0;
string += rhs.0;
ObjectDir(string)
}
}
impl<'dir1, 'dir2: 'dir1> AddAssign<ObjectDir<'dir2>> for ObjectDir<'dir1> {
fn add_assign(&mut self, rhs: ObjectDir<'dir2>) {
*self.as_mut() += rhs.as_ref();
}
}
impl<'file, 'dir: 'file> Add<ObjectPathInner<'file>> for ObjectDir<'dir> {
type Output = ObjectPathInner<'file>;
fn add(self, rhs: ObjectPathInner<'file>) -> Self::Output {
let mut string = self.0;
string += rhs.0;
ObjectPathInner(string)
}
}
impl<'a> ObjectDir<'a> {
pub fn new<'b: 'a>(val: impl Into<Cow<'b, str>>) -> Result<Self, InvalidObjectDir> {
let val = val.into();
if val.starts_with('/') || val.starts_with('.') || !val.ends_with('/') {
return Err(InvalidObjectDir::new());
}
if !val.chars().all(|c| c != '\\') {
return Err(InvalidObjectDir::new());
}
Ok(Self(val))
}
pub const unsafe fn from_static(secret: &'a str) -> Self {
Self(Cow::Borrowed(secret))
}
}
impl TryFrom<String> for ObjectDir<'_> {
type Error = InvalidObjectDir;
fn try_from(val: String) -> Result<Self, Self::Error> {
Self::new(val)
}
}
impl<'a: 'b, 'b> TryFrom<&'a str> for ObjectDir<'b> {
type Error = InvalidObjectDir;
fn try_from(val: &'a str) -> Result<Self, Self::Error> {
Self::new(val)
}
}
impl FromStr for ObjectDir<'_> {
type Err = InvalidObjectDir;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with('/') || s.starts_with('.') || !s.ends_with('/') {
return Err(InvalidObjectDir::new());
}
if !s.chars().all(|c| c != '\\') {
return Err(InvalidObjectDir::new());
}
Ok(Self(Cow::Owned(s.to_owned())))
}
}
impl TryFrom<&Path> for ObjectDir<'_> {
type Error = InvalidObjectDir;
fn try_from(value: &Path) -> Result<Self, Self::Error> {
let val = value.to_str().ok_or(InvalidObjectDir::new())?;
if std::path::MAIN_SEPARATOR != '/' {
val.replace(std::path::MAIN_SEPARATOR, "/").parse()
} else {
val.parse()
}
}
}
pub struct InvalidObjectDir {
_priv: (),
}
impl InvalidObjectDir {
pub(crate) fn new() -> InvalidObjectDir {
InvalidObjectDir { _priv: () }
}
}
impl Display for InvalidObjectDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"object-dir must end with `/`, and not start with `/`,`.`"
)
}
}
impl Debug for InvalidObjectDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InvalidObjectDir").finish()
}
}
impl std::error::Error for InvalidObjectDir {}
pub trait FromOss {
fn from_oss(endpoint: &EndPoint, bucket: &BucketName, path: &ObjectPath) -> Self;
}
impl FromOss for Url {
fn from_oss(endpoint: &EndPoint, bucket: &BucketName, path: &ObjectPath) -> Self {
let mut end_url = endpoint.to_url();
let host = end_url.host_str();
let mut name_str = bucket.to_string() + ".";
let new_host = host.map(|h| {
name_str.push_str(h);
&*name_str
});
end_url
.set_host(new_host)
.unwrap_or_else(|_| panic!("set host failed: host: {}", new_host.unwrap_or("none")));
end_url.set_object_path(path);
end_url
}
}
pub type CommonPrefixes = Vec<ObjectDir<'static>>;