use teloxide_core::types::{User, UserId};
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn bold(s: &str) -> String {
format!("<b>{s}</b>")
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn blockquote(s: &str) -> String {
format!("<blockquote>{s}</blockquote>")
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn italic(s: &str) -> String {
format!("<i>{s}</i>")
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn underline(s: &str) -> String {
format!("<u>{s}</u>")
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn strike(s: &str) -> String {
format!("<s>{s}</s>")
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn link(url: &str, text: &str) -> String {
format!("<a href=\"{}\">{}</a>", escape(url), escape(text))
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn user_mention(user_id: UserId, text: &str) -> String {
link(format!("tg://user?id={user_id}").as_str(), text)
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn code_block(code: &str) -> String {
format!("<pre>{}</pre>", escape(code))
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn code_block_with_lang(code: &str, lang: &str) -> String {
format!(
"<pre><code class=\"language-{}\">{}</code></pre>",
escape(lang).replace('"', """),
escape(code)
)
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn code_inline(s: &str) -> String {
format!("<code>{}</code>", escape(s))
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn escape(s: &str) -> String {
s.chars().fold(String::with_capacity(s.len()), |mut s, c| {
match c {
'&' => s.push_str("&"),
'<' => s.push_str("<"),
'>' => s.push_str(">"),
c => s.push(c),
}
s
})
}
#[must_use = "This function returns a new string, rather than mutating the argument, so calling it \
without using its output does nothing useful"]
pub fn user_mention_or_link(user: &User) -> String {
match user.mention() {
Some(mention) => mention,
None => link(user.url().as_str(), &user.full_name()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bold() {
assert_eq!(bold(" foobar "), "<b> foobar </b>");
assert_eq!(bold(" <i>foobar</i> "), "<b> <i>foobar</i> </b>");
assert_eq!(bold("<s>(`foobar`)</s>"), "<b><s>(`foobar`)</s></b>");
}
#[test]
fn test_italic() {
assert_eq!(italic(" foobar "), "<i> foobar </i>");
assert_eq!(italic(" <b>foobar</b> "), "<i> <b>foobar</b> </i>");
assert_eq!(italic("<s>(`foobar`)</s>"), "<i><s>(`foobar`)</s></i>");
}
#[test]
fn test_underline() {
assert_eq!(underline(" foobar "), "<u> foobar </u>");
assert_eq!(underline(" <b>foobar</b> "), "<u> <b>foobar</b> </u>");
assert_eq!(underline("<s>(`foobar`)</s>"), "<u><s>(`foobar`)</s></u>");
}
#[test]
fn test_strike() {
assert_eq!(strike(" foobar "), "<s> foobar </s>");
assert_eq!(strike(" <b>foobar</b> "), "<s> <b>foobar</b> </s>");
assert_eq!(strike("<b>(`foobar`)</b>"), "<s><b>(`foobar`)</b></s>");
}
#[test]
fn test_link() {
assert_eq!(
link("https://www.google.com/?q=foo&l=ru", "<google>"),
"<a href=\"https://www.google.com/?q=foo&l=ru\"><google></a>",
);
}
#[test]
fn test_user_mention() {
assert_eq!(
user_mention(UserId(123_456_789), "<pwner666>"),
"<a href=\"tg://user?id=123456789\"><pwner666></a>",
);
}
#[test]
fn test_code_block() {
assert_eq!(
code_block("<p>pre-'formatted'\n & fixed-width \\code `block`</p>"),
"<pre><p>pre-'formatted'\n & fixed-width \\code `block`</p></pre>"
);
}
#[test]
fn test_code_block_with_lang() {
assert_eq!(
code_block_with_lang(
"<p>pre-'formatted'\n & fixed-width \\code `block`</p>",
"<html>\"",
),
concat!(
"<pre><code class=\"language-<html>"\">",
"<p>pre-'formatted'\n & fixed-width \\code `block`</p>",
"</code></pre>",
)
);
}
#[test]
fn test_code_inline() {
assert_eq!(
code_inline("<span class=\"foo\">foo & bar</span>"),
"<code><span class=\"foo\">foo & bar</span></code>",
);
}
#[test]
fn test_escape() {
assert_eq!(
escape(" <title>Foo & Bar</title> "),
" <title>Foo & Bar</title> "
);
assert_eq!(escape("<p>你好 & 再見</p>"), "<p>你好 & 再見</p>");
assert_eq!(escape("'foo\""), "'foo\"");
}
#[test]
fn user_mention_link() {
let user_with_username = User {
id: UserId(0),
is_bot: false,
first_name: "".to_string(),
last_name: None,
username: Some("abcd".to_string()),
language_code: None,
is_premium: false,
added_to_attachment_menu: false,
};
assert_eq!(user_mention_or_link(&user_with_username), "@abcd");
let user_without_username = User {
id: UserId(123_456_789),
is_bot: false,
first_name: "Name".to_string(),
last_name: None,
username: None,
language_code: None,
is_premium: false,
added_to_attachment_menu: false,
};
assert_eq!(
user_mention_or_link(&user_without_username),
r#"<a href="tg://user/?id=123456789">Name</a>"#
)
}
}