use {
std::{
convert::{
TryFrom,
TryInto,
},
fmt,
str::FromStr,
},
css_color_parser::ColorParseError,
url::Url,
crate::{
ContentItem,
Menu,
},
};
#[cfg(feature = "base64")] use base64::{
Engine as _,
engine::general_purpose::STANDARD as BASE64,
};
#[cfg(all(feature = "base64", feature = "image"))] use {
std::io::Cursor,
image::{
DynamicImage,
ImageError,
ImageOutputFormat::Png,
ImageResult,
},
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Color {
pub(crate) light: css_color_parser::Color,
pub(crate) dark: Option<css_color_parser::Color>,
}
impl From<css_color_parser::Color> for Color {
fn from(light: css_color_parser::Color) -> Color {
Color { light, dark: None }
}
}
impl FromStr for Color {
type Err = ColorParseError;
fn from_str(s: &str) -> Result<Color, ColorParseError> {
Ok(Color {
light: s.parse()?,
dark: None,
})
}
}
impl<'a> TryFrom<&'a str> for Color {
type Error = ColorParseError;
fn try_from(s: &str) -> Result<Color, ColorParseError> {
s.parse()
}
}
#[cfg(feature = "css-colors")]
macro_rules! css_color_try_into_color {
($t:ty) => {
#[cfg_attr(docsrs, doc(cfg(feature = "css-colors")))]
impl TryFrom<$t> for Color {
type Error = ColorParseError;
fn try_from(color: $t) -> Result<Color, ColorParseError> {
Ok(Color {
light: color.to_string().parse()?,
dark: None,
})
}
}
};
}
#[cfg(feature = "css-colors")] css_color_try_into_color!(css_colors::RGB);
#[cfg(feature = "css-colors")] css_color_try_into_color!(css_colors::RGBA);
#[cfg(feature = "css-colors")] css_color_try_into_color!(css_colors::HSL);
#[cfg(feature = "css-colors")] css_color_try_into_color!(css_colors::HSLA);
#[cfg(feature = "serenity")]
#[cfg_attr(docsrs, doc(cfg(feature = "serenity")))]
impl From<serenity::utils::Colour> for Color {
fn from(c: serenity::utils::Colour) -> Color {
Color {
light: css_color_parser::Color {
r: c.r(),
g: c.g(),
b: c.b(),
a: 1.0,
},
dark: None,
}
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#{:02x}{:02x}{:02x}", self.light.r, self.light.g, self.light.b)?;
if let Some(dark) = self.dark {
write!(f, ",#{:02x}{:02x}{:02x}", dark.r, dark.g, dark.b)?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum Extra {
Alternate(Box<ContentItem>), Submenu(Menu),
}
pub trait IntoUrl {
fn into_url(self) -> Result<Url, url::ParseError>;
}
impl IntoUrl for Url {
fn into_url(self) -> Result<Url, url::ParseError> {
Ok(self)
}
}
impl IntoUrl for String {
fn into_url(self) -> Result<Url, url::ParseError> {
Url::parse(&self)
}
}
impl<'a> IntoUrl for &'a str {
fn into_url(self) -> Result<Url, url::ParseError> {
Url::parse(self)
}
}
#[derive(Debug)]
pub struct Params {
pub(crate) cmd: String,
pub(crate) params: Vec<String>,
}
impl Params {
#[doc(hidden)] pub fn new(cmd: String, params: Vec<String>) -> Self {
Self { cmd, params }
}
}
macro_rules! params_from {
($n:literal$(, $elt:ident: $t:ident)*) => {
impl<T: ToString> From<[T; $n]> for Params {
fn from([cmd, $($elt),*]: [T; $n]) -> Params {
Params {
cmd: cmd.to_string(),
params: vec![$($elt.to_string()),*],
}
}
}
impl<Cmd: ToString, $($t: ToString),*> From<(Cmd, $($t),*)> for Params {
fn from((cmd, $($elt),*): (Cmd, $($t),*)) -> Params {
Params {
cmd: cmd.to_string(),
params: vec![$($elt.to_string()),*],
}
}
}
};
}
params_from!(1);
params_from!(2, param1: A);
params_from!(3, param1: A, param2: B);
params_from!(4, param1: A, param2: B, param3: C);
params_from!(5, param1: A, param2: B, param3: C, param4: D);
params_from!(6, param1: A, param2: B, param3: C, param4: D, param5: E);
impl<'a, T: ToString> TryFrom<&'a [T]> for Params {
type Error = &'a [T];
fn try_from(slice: &[T]) -> Result<Params, &[T]> {
match slice {
[cmd] => Ok(Params { cmd: cmd.to_string(), params: Vec::default() }),
[cmd, param1] => Ok(Params { cmd: cmd.to_string(), params: vec![param1.to_string()] }),
[cmd, param1, param2] => Ok(Params { cmd: cmd.to_string(), params: vec![param1.to_string(), param2.to_string()] }),
[cmd, param1, param2, param3] => Ok(Params { cmd: cmd.to_string(), params: vec![param1.to_string(), param2.to_string(), param3.to_string()] }),
[cmd, param1, param2, param3, param4] => Ok(Params { cmd: cmd.to_string(), params: vec![param1.to_string(), param2.to_string(), param3.to_string(), param4.to_string()] }),
[cmd, param1, param2, param3, param4, param5] => Ok(Params { cmd: cmd.to_string(), params: vec![param1.to_string(), param2.to_string(), param3.to_string(), param4.to_string(), param5.to_string()] }),
slice => Err(slice),
}
}
}
impl<T: ToString> TryFrom<Vec<T>> for Params {
type Error = Vec<T>;
fn try_from(mut v: Vec<T>) -> Result<Params, Vec<T>> {
match v.len() {
1..=6 => Ok(Params {
cmd: v.remove(0).to_string(),
params: v.into_iter().map(|x| x.to_string()).collect(),
}),
_ => Err(v),
}
}
}
#[derive(Debug)]
pub struct Command {
pub(crate) params: Params,
pub(crate) terminal: bool,
}
impl Command {
pub fn terminal(args: impl Into<Params>) -> Command {
Command {
params: args.into(),
terminal: true,
}
}
pub fn try_from<P: TryInto<Params>>(args: P) -> Result<Command, P::Error> {
Ok(Command {
params: args.try_into()?,
terminal: false,
})
}
pub fn try_terminal<P: TryInto<Params>>(args: P) -> Result<Command, P::Error> {
Ok(Command {
params: args.try_into()?,
terminal: true,
})
}
}
impl<P: Into<Params>> From<P> for Command {
fn from(args: P) -> Command {
Command {
params: args.into(),
terminal: false,
}
}
}
#[derive(Debug, Clone)]
pub struct Image {
pub base64_data: String,
pub is_template: bool,
}
impl Image {
pub fn template<T: TryInto<Image>>(img: T) -> Result<Image, T::Error> {
let mut result = img.try_into()?;
result.is_template = true;
Ok(result)
}
}
impl From<String> for Image {
fn from(base64_data: String) -> Image {
Image {
base64_data,
is_template: false,
}
}
}
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
impl From<Vec<u8>> for Image {
fn from(input: Vec<u8>) -> Image {
Image {
base64_data: BASE64.encode(&input),
is_template: false,
}
}
}
#[cfg(feature = "base64")]
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
impl<T: ?Sized + AsRef<[u8]>> From<&T> for Image {
fn from(input: &T) -> Image {
Image {
base64_data: BASE64.encode(input),
is_template: false,
}
}
}
#[cfg(all(feature = "base64", feature = "image"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "base64", feature = "image"))))]
impl TryFrom<DynamicImage> for Image {
type Error = ImageError;
fn try_from(img: DynamicImage) -> ImageResult<Image> {
let mut buf = Cursor::<Vec<_>>::default();
img.write_to(&mut buf, Png)?;
Ok(Image::from(buf.into_inner()))
}
}