use std::{fmt, str::FromStr};
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct UploadId(String);
impl UploadId {
pub const MAX_LENGTH: usize = 2_048;
pub fn new(value: impl Into<String>) -> Result<Self, UploadIdError> {
let value = value.into();
if value.is_empty() {
return Err(UploadIdError::Empty);
}
if value.len() > Self::MAX_LENGTH {
return Err(UploadIdError::TooLong {
length: value.len(),
maximum: Self::MAX_LENGTH,
});
}
if let Some(index) = value.bytes().position(|byte| byte.is_ascii_control()) {
return Err(UploadIdError::AsciiControl { index });
}
Ok(Self(value))
}
pub fn expose(&self) -> &str {
&self.0
}
pub fn as_str(&self) -> &str {
self.expose()
}
}
impl fmt::Debug for UploadId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("UploadId([REDACTED])")
}
}
impl fmt::Display for UploadId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[REDACTED]")
}
}
impl FromStr for UploadId {
type Err = UploadIdError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::new(value)
}
}
impl TryFrom<String> for UploadId {
type Error = UploadIdError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum UploadIdError {
#[error("multipart upload ID cannot be empty")]
Empty,
#[error("multipart upload ID is {length} bytes; the maximum is {maximum}")]
TooLong {
length: usize,
maximum: usize,
},
#[error("multipart upload ID contains an ASCII control at byte {index}")]
AsciiControl {
index: usize,
},
}