twilight_util/builder/embed/
image_source.rsuse std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
#[derive(Debug)]
pub struct ImageSourceAttachmentError {
kind: ImageSourceAttachmentErrorType,
}
impl ImageSourceAttachmentError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &ImageSourceAttachmentErrorType {
&self.kind
}
#[allow(clippy::unused_self)]
#[must_use = "consuming the error and retrieving the source has no effect if left unused"]
pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
None
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(
self,
) -> (
ImageSourceAttachmentErrorType,
Option<Box<dyn Error + Send + Sync>>,
) {
(self.kind, None)
}
}
impl Display for ImageSourceAttachmentError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
ImageSourceAttachmentErrorType::ExtensionEmpty { .. } => {
f.write_str("the extension is empty")
}
ImageSourceAttachmentErrorType::ExtensionMissing { .. } => {
f.write_str("the extension is missing")
}
}
}
}
impl Error for ImageSourceAttachmentError {}
#[derive(Debug)]
#[non_exhaustive]
pub enum ImageSourceAttachmentErrorType {
ExtensionEmpty,
ExtensionMissing,
}
#[derive(Debug)]
pub struct ImageSourceUrlError {
kind: ImageSourceUrlErrorType,
}
impl ImageSourceUrlError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &ImageSourceUrlErrorType {
&self.kind
}
#[allow(clippy::unused_self)]
#[must_use = "consuming the error and retrieving the source has no effect if left unused"]
pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
None
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(
self,
) -> (
ImageSourceUrlErrorType,
Option<Box<dyn Error + Send + Sync>>,
) {
(self.kind, None)
}
}
impl Display for ImageSourceUrlError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
ImageSourceUrlErrorType::ProtocolUnsupported { .. } => {
f.write_str("the provided URL's protocol is unsupported by Discord")
}
}
}
}
impl Error for ImageSourceUrlError {}
#[derive(Debug)]
#[non_exhaustive]
pub enum ImageSourceUrlErrorType {
ProtocolUnsupported {
url: String,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct ImageSource(pub(super) String);
impl ImageSource {
pub fn attachment(filename: impl AsRef<str>) -> Result<Self, ImageSourceAttachmentError> {
let filename = filename.as_ref();
let dot = filename.rfind('.').ok_or(ImageSourceAttachmentError {
kind: ImageSourceAttachmentErrorType::ExtensionMissing,
})? + 1;
if filename
.get(dot..)
.ok_or(ImageSourceAttachmentError {
kind: ImageSourceAttachmentErrorType::ExtensionMissing,
})?
.is_empty()
{
return Err(ImageSourceAttachmentError {
kind: ImageSourceAttachmentErrorType::ExtensionEmpty,
});
}
Ok(Self(format!("attachment://{filename}")))
}
pub fn url(url: impl Into<String>) -> Result<Self, ImageSourceUrlError> {
let url = url.into();
if !url.starts_with("https:") && !url.starts_with("http:") {
return Err(ImageSourceUrlError {
kind: ImageSourceUrlErrorType::ProtocolUnsupported { url },
});
}
Ok(Self(url))
}
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::{assert_fields, assert_impl_all};
use std::fmt::Debug;
assert_impl_all!(ImageSourceAttachmentErrorType: Debug, Send, Sync);
assert_impl_all!(ImageSourceAttachmentError: Error, Send, Sync);
assert_impl_all!(ImageSourceUrlErrorType: Debug, Send, Sync);
assert_impl_all!(ImageSourceUrlError: Error, Send, Sync);
assert_fields!(ImageSourceUrlErrorType::ProtocolUnsupported: url);
assert_impl_all!(ImageSource: Clone, Debug, Eq, PartialEq, Send, Sync);
#[test]
fn attachment() -> Result<(), Box<dyn Error>> {
assert!(matches!(
ImageSource::attachment("abc").unwrap_err().kind(),
ImageSourceAttachmentErrorType::ExtensionMissing
));
assert!(matches!(
ImageSource::attachment("abc.").unwrap_err().kind(),
ImageSourceAttachmentErrorType::ExtensionEmpty
));
assert_eq!(
ImageSource::attachment("abc.png")?,
ImageSource("attachment://abc.png".to_owned()),
);
Ok(())
}
#[test]
fn url() -> Result<(), Box<dyn Error>> {
assert!(matches!(
ImageSource::url("ftp://example.com/foo").unwrap_err().kind(),
ImageSourceUrlErrorType::ProtocolUnsupported { url }
if url == "ftp://example.com/foo"
));
assert_eq!(
ImageSource::url("https://example.com")?,
ImageSource("https://example.com".to_owned()),
);
assert_eq!(
ImageSource::url("http://example.com")?,
ImageSource("http://example.com".to_owned()),
);
Ok(())
}
}