use crate::utils::escape::escape;
use crate::{notification::ToastVisualableXML, ToXML};
use super::{TextOrImageElement, VisualElement};
pub enum Placement {
AppLogoOverride,
Hero,
None,
}
impl ToString for Placement {
fn to_string(&self) -> String {
match self {
Placement::AppLogoOverride => "placement=\"appLogoOverride\"".to_string(),
Placement::Hero => "placement=\"hero\"".to_string(),
Placement::None => "".to_string(),
}
}
}
#[derive(Debug, Clone, Default)]
pub enum ImageCrop {
#[default]
Default,
None,
Circle,
}
impl ToString for ImageCrop {
fn to_string(&self) -> String {
match self {
ImageCrop::Default => "".to_string(),
ImageCrop::Circle => "hint-crop=\"circle\"".to_string(),
ImageCrop::None => "hint-crop=\"none\"".to_string(),
}
}
}
#[derive(Debug, Clone, Default)]
pub enum AdaptiveImageAlign {
#[default]
Default,
Stretch,
Left,
Center,
Right,
}
impl ToString for AdaptiveImageAlign {
fn to_string(&self) -> String {
match self {
AdaptiveImageAlign::Default => "".to_string(),
AdaptiveImageAlign::Stretch => "hint-align=\"stretch\"".to_string(),
AdaptiveImageAlign::Left => "hint-align=\"left\"".to_string(),
AdaptiveImageAlign::Center => "hint-align=\"center\"".to_string(),
AdaptiveImageAlign::Right => "hint-align=\"right\"".to_string(),
}
}
}
#[allow(non_snake_case)]
pub struct Image {
pub id: u64,
pub src: String,
pub alt: Option<String>,
pub add_image_query: bool,
pub placement: Placement,
pub crop: ImageCrop,
pub no_margin: bool,
pub align: AdaptiveImageAlign,
}
impl TextOrImageElement for Image {}
fn guess_src(src: String) -> String {
let protocols = [
"https://",
"http://",
"file:///",
"ms-appx:///",
"ms-appdata:///local/",
];
if !(protocols.iter().any(|x| src.starts_with(x))) {
return format!("file:///{src}");
}
src
}
impl Image {
pub fn create(id: u64, src: &str) -> Self {
Self::new(
id,
escape(src),
None,
false,
Placement::None,
ImageCrop::Default,
false,
)
}
pub fn new<T: Into<String>>(
id: u64,
src: T,
alt: Option<&str>,
add_image_query: bool,
placement: Placement,
crop: ImageCrop,
no_margin: bool,
) -> Self {
Self {
id,
add_image_query,
src: guess_src(src.into()),
alt: alt.map(|x| escape(x).to_string()),
placement,
crop,
align: AdaptiveImageAlign::Default,
no_margin,
}
}
}
impl Image {
pub fn with_margin(mut self, margin: bool) -> Self {
self.no_margin = !margin;
self
}
pub fn with_align(mut self, align: AdaptiveImageAlign) -> Self {
self.align = align;
self
}
pub fn with_alt<S: AsRef<str>>(mut self, alt: S) -> Self {
self.alt = Some(escape(alt.as_ref()).into());
self
}
pub fn without_image_query(mut self) -> Self {
self.add_image_query = false;
self
}
pub fn with_image_query(mut self) -> Self {
self.add_image_query = true;
self
}
pub fn with_crop(mut self, crop: ImageCrop) -> Self {
self.crop = crop;
self
}
pub fn with_placement(mut self, placement: Placement) -> Self {
self.placement = placement;
self
}
}
impl VisualElement for Image {}
impl ToastVisualableXML for Image {}
impl ToXML for Image {
fn to_xml(&self) -> String {
format!(
r#"
<image id="{id}" {margin} {align} src="{src}" {add_image_query} {alt} {placement} {crop} />
"#,
align = self.align.to_string(),
margin = match self.no_margin {
true => "hint-remove-margin=\"true\"".to_string(),
false => "".to_string(),
},
id = self.id,
src = self.src,
alt = self
.alt
.clone()
.map_or_else(|| string!(""), |x| format!("alt=\"{x}\"")),
add_image_query = if self.add_image_query {
"addImageQuery=\"True\""
} else {
""
},
placement = self.placement.to_string(),
crop = self.crop.to_string()
)
}
}