use std::borrow::Cow;
use std::iter::FusedIterator;
use std::{fmt, str};
use crate::arguments::{ArgumentScanner, FromArgs};
use crate::keyword::SendKeyword;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Send<S = String> {
pub href: S,
pub hint: S,
pub expire: Option<S>,
pub prompt: bool,
}
impl Send {
pub const EMBED_ENTITY: &'static str = "&text;";
fn embed_text(input: &str, text: &str) -> String {
if input.is_empty() {
text.to_owned()
} else {
input.replace(Send::EMBED_ENTITY, text)
}
}
}
impl<S> Send<S> {
pub fn map_text<T, F>(self, mut f: F) -> Send<T>
where
F: FnMut(S) -> T,
{
Send {
href: f(self.href),
hint: f(self.hint),
expire: self.expire.map(f),
prompt: self.prompt,
}
}
}
impl_into_owned!(Send);
impl<S: AsRef<str>> Send<S> {
pub fn borrow_text(&self) -> Send<&str> {
Send {
href: self.href.as_ref(),
hint: self.hint.as_ref(),
expire: self.expire.as_ref().map(AsRef::as_ref),
prompt: self.prompt,
}
}
#[must_use = "function returns a new link"]
pub fn for_text(&self, text: &str) -> Send<String> {
let href_str = self.href.as_ref();
let hint_str = self.hint.as_ref();
let href = Send::embed_text(href_str, text);
let hint = if hint_str == href_str {
href.clone()
} else {
Send::embed_text(hint_str, text)
};
Send {
href,
hint,
expire: self
.expire
.as_ref()
.map(|expire| expire.as_ref().to_owned()),
prompt: self.prompt,
}
}
pub fn is_menu(&self) -> bool {
self.href.as_ref().as_bytes().contains(&b'|')
}
}
impl_partial_eq!(Send);
impl<S: Clone> From<S> for Send<S> {
fn from(value: S) -> Self {
Self {
href: value.clone(),
hint: value,
expire: None,
prompt: false,
}
}
}
impl Send<String> {
pub fn menu(&self) -> SendMenu<'_> {
SendMenu {
commands: self.href.split('|'),
labels: self.hint.split('|'),
}
}
}
impl Send<Cow<'_, str>> {
pub fn menu(&self) -> SendMenu<'_> {
SendMenu {
commands: self.href.split('|'),
labels: self.hint.split('|'),
}
}
}
impl<'a> Send<&'a str> {
pub fn menu(&self) -> SendMenu<'a> {
let mut labels = self.hint.split('|');
labels.next();
SendMenu {
commands: self.href.split('|'),
labels,
}
}
}
impl<'a, S: AsRef<str> + Clone + Default> FromArgs<'a, S> for Send<S> {
fn from_args<A: ArgumentScanner<'a, Decoded = S>>(scanner: A) -> crate::Result<Self> {
let mut scanner = scanner.with_keywords();
let href = scanner.get_next_or("href")?.unwrap_or_default();
let hint = scanner.get_next_or("hint")?.unwrap_or_else(|| href.clone());
let expire = scanner.get_next_or("expire")?;
let keywords = scanner.into_keywords()?;
Ok(Self {
href,
hint,
expire,
prompt: keywords.contains(SendKeyword::Prompt),
})
}
}
impl_from_str!(Send);
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct SendMenuItem<'a> {
pub command: &'a str,
pub label: &'a str,
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct SendMenu<'a> {
commands: str::Split<'a, char>,
labels: str::Split<'a, char>,
}
impl fmt::Debug for SendMenu<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.clone()).finish()
}
}
impl<'a> Iterator for SendMenu<'a> {
type Item = SendMenuItem<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let command = self.commands.next()?;
Some(SendMenuItem {
command,
label: self.labels.next().unwrap_or(command),
})
}
}
impl FusedIterator for SendMenu<'_> {}
impl<S: AsRef<str>> fmt::Display for Send<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Send {
mut href,
mut hint,
expire,
prompt,
} = self.borrow_text();
if hint == href {
hint = "";
}
if href == Send::EMBED_ENTITY {
href = "";
}
crate::display::ElementFormatter {
name: "SEND",
arguments: &[&href, &hint, &expire],
keywords: &[("PROMPT", prompt)],
}
.fmt(f)
}
}