use std::borrow::Cow;
use std::cmp::Ordering;
use heck::ToKebabCase;
use pulldown_cmark::LinkType;
#[derive(Debug, Default)]
pub(crate) struct LinkBuilder<'a> {
link: Link<'a>,
}
impl<'a> LinkBuilder<'a> {
pub(crate) fn from_type_url_title(
link_type: LinkType,
url: Cow<'a, str>,
title: Cow<'a, str>,
label: Cow<'a, str>,
) -> Self {
Self {
link: Link {
link_type: Some(link_type),
url: if !url.is_empty() { Some(url) } else { None },
title: if !title.is_empty() { Some(title) } else { None },
label: if !label.is_empty() { Some(label) } else { None },
..Link::default()
},
}
}
pub(crate) fn set_url(mut self, url: Cow<'a, str>) -> Self {
if !url.is_empty() {
self.link.url = Some(url);
}
self
}
pub(crate) fn add_text(mut self, text: Cow<'a, str>) -> Self {
if !text.is_empty() {
self.link.text = Some(format!("{}{}", self.link.text.unwrap_or_default(), text).into());
}
self
}
pub(crate) fn set_label(mut self, label: Cow<'a, str>) -> Self {
if !label.is_empty() {
self.link.label = Some(label);
}
self
}
pub(crate) fn set_image(
self,
image_link_type: LinkType,
image_url: Cow<'a, str>,
image_title: Cow<'a, str>,
image_label: Cow<'a, str>,
) -> Self {
Self {
link: Link {
image_link_type: Some(image_link_type),
image_url: if !image_url.is_empty() {
Some(image_url)
} else {
self.link.image_url
},
image_title: if !image_title.is_empty() {
Some(image_title)
} else {
self.link.image_title
},
image_label: if !image_label.is_empty() {
Some(image_label)
} else {
self.link.image_label
},
..self.link
},
}
}
pub(crate) fn set_image_url(mut self, image_url: Cow<'a, str>) -> Self {
if !image_url.is_empty() {
self.link.image_url = Some(image_url);
}
self
}
pub(crate) fn add_image_alt_text(mut self, image_alt_text: Cow<'a, str>) -> Self {
if !image_alt_text.is_empty() {
self.link.image_alt_text =
Some(self.link.image_alt_text.unwrap_or_default() + image_alt_text)
}
self
}
pub(crate) fn build(self) -> Link<'a> {
self.link
}
}
#[derive(Debug, Default, Clone)]
pub(crate) struct Link<'a> {
link_type: Option<LinkType>,
text: Option<Cow<'a, str>>, label: Option<Cow<'a, str>>, url: Option<Cow<'a, str>>, title: Option<Cow<'a, str>>,
#[allow(dead_code)]
image_link_type: Option<LinkType>,
image_alt_text: Option<Cow<'a, str>>,
image_label: Option<Cow<'a, str>>,
image_url: Option<Cow<'a, str>>,
image_title: Option<Cow<'a, str>>,
}
impl<'a> Link<'a> {
pub(crate) fn get_link_type(&self) -> Option<LinkType> {
self.link_type
}
fn get_text(&self) -> Cow<'a, str> {
self.text.clone().unwrap_or(Cow::from("TODO"))
}
pub(crate) fn get_url(&self) -> Cow<'a, str> {
if let Some(u) = &self.url {
u.clone()
} else {
Cow::from(String::new())
}
}
fn get_url_and_title(&self) -> Cow<'a, str> {
if let Some(u) = &self.url {
if let Some(t) = &self.title {
format!("{} \"{}\"", u, t).into()
} else {
u.clone()
}
} else {
Cow::from(String::new())
}
}
fn get_label(&self) -> Cow<'a, str> {
if let Some(label) = &self.label {
label.clone()
} else if let Some(txt) = &self.text {
txt.to_kebab_case().into()
} else {
"XYZ".into()
}
}
pub(crate) fn to_inline_link(&self) -> Cow<'a, str> {
format!("[{}]( {} )", self.get_text(), self.get_url_and_title()).into()
}
pub(crate) fn to_reference_link(&self) -> Cow<'a, str> {
let txt: String = self.get_text().into();
let label: String = self.get_label().into();
if txt == label {
format!("[{txt}]").into()
} else {
format!("[{txt}][{label}]").into()
}
}
pub(crate) fn to_reference_definition(&self) -> Cow<'a, str> {
format!("[{}]: {}", self.get_label(), self.get_url_and_title()).into()
}
fn get_badge_alt_text(&self) -> Cow<'a, str> {
if let Some(alt_txt) = &self.image_alt_text {
alt_txt.clone()
} else if let Some(img_lbl) = &self.image_label {
img_lbl.clone()
} else if let Some(lbl) = &self.label {
lbl.clone()
} else {
"TODO".into()
}
}
fn get_badge_label(&self) -> Cow<'a, str> {
if let Some(ref img_lbl) = self.image_label {
img_lbl.clone()
} else if let Some(ref lbl) = self.label {
format!("{}-badge", lbl).into()
} else if let Some(ref alt_txt) = self.image_alt_text {
alt_txt.clone()
} else {
"ABC".into() }
}
fn get_badge_url_and_title(&self) -> Cow<'a, str> {
if let Some(ref u) = self.image_url {
if let Some(ref t) = self.image_title {
format!("{} \"{}\"", u, t).into()
} else {
u.clone()
}
} else {
Cow::from(String::new())
}
}
pub(crate) fn to_link_with_badge(&self) -> Cow<'a, str> {
format!(
"[![{}][{}]][{}]",
self.get_badge_alt_text(),
self.get_badge_label(),
self.get_label()
)
.into()
}
pub(crate) fn to_badge_reference_definition(&self) -> Cow<'a, str> {
format!(
"[{}]: {}",
self.get_badge_label(),
self.get_badge_url_and_title()
)
.into()
}
}
impl<'a> PartialOrd for Link<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for Link<'a> {
fn cmp(&self, other: &Self) -> Ordering {
self.label
.cmp(&other.label)
.then(self.url.cmp(&other.url))
.then(self.title.cmp(&other.title))
}
}
impl<'a> PartialEq for Link<'a> {
fn eq(&self, other: &Self) -> bool {
(self.label == other.label) && (self.url == other.url) && (self.title == other.title)
}
}
impl<'a> Eq for Link<'a> {}
#[cfg(test)]
mod test {
}