mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-08 19:33:53 +01:00
Write markdown text formatting utils
This commit is contained in:
parent
eb44cd8b44
commit
066271b397
1 changed files with 158 additions and 6 deletions
|
@ -1,5 +1,58 @@
|
|||
use std::string::String;
|
||||
|
||||
pub fn bold(s: &str) -> String {
|
||||
wrap(s, "*", "*")
|
||||
}
|
||||
|
||||
pub fn italic(s: &str) -> String {
|
||||
if s.starts_with("__") && s.ends_with("__") {
|
||||
return wrap(&s[..s.len() - 1], "_", r"\r__");
|
||||
}
|
||||
wrap(s, "_", "_")
|
||||
}
|
||||
|
||||
pub fn underline(s: &str) -> String {
|
||||
// In case of ambiguity between italic and underline entities
|
||||
// ‘__’ is always greadily treated from left to right as beginning or end of underline entity,
|
||||
// so instead of ___italic underline___ we should use ___italic underline_\r__,
|
||||
// where \r is a character with code 13, which will be ignored.
|
||||
if s.starts_with("_") && s.ends_with("_") {
|
||||
return wrap(s, "__", r"\r__");
|
||||
}
|
||||
wrap(s, "__", "__")
|
||||
}
|
||||
|
||||
pub fn strike(s: &str) -> String {
|
||||
wrap(s, "~", "~")
|
||||
}
|
||||
|
||||
pub fn link(url: &str, text: &str) -> String {
|
||||
let mut out = String::with_capacity(url.len() + text.len() + 4);
|
||||
out.push_str(wrap(text, "[", "]").as_str());
|
||||
out.push_str(wrap(escape_link_url(url).as_str(), "(", ")").as_str());
|
||||
out
|
||||
}
|
||||
|
||||
pub fn user_mention(user_id: i32, text: &str) -> String {
|
||||
link(format!("tg://user?id={}", user_id).as_str(), text)
|
||||
}
|
||||
|
||||
pub fn code_block(code: &str) -> String {
|
||||
code_block_with_lang(code, "")
|
||||
}
|
||||
|
||||
pub fn code_block_with_lang(code: &str, lang: &str) -> String {
|
||||
wrap(
|
||||
escape_code(code).as_str(),
|
||||
format!("```{}\n", lang).as_str(),
|
||||
"\n```",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn code_inline(s: &str) -> String {
|
||||
wrap(escape_code(s).as_str(), "`", "`")
|
||||
}
|
||||
|
||||
// Escapes all markdown special characters in the string
|
||||
// https://core.telegram.org/bots/api#markdownv2-style
|
||||
pub fn escape(s: &str) -> String {
|
||||
|
@ -27,15 +80,114 @@ pub fn escape_link_url(s: &str) -> String {
|
|||
s.replace("`", r"\`").replace(")", r"\)")
|
||||
}
|
||||
|
||||
// Escapes all markdown special characters in the code block
|
||||
pub fn escape_code_block(s: &str) -> String {
|
||||
// Escapes all markdown special characters in the code block or line
|
||||
pub fn escape_code(s: &str) -> String {
|
||||
s.replace(r"\", r"\\").replace("`", r"\`")
|
||||
}
|
||||
|
||||
fn wrap(s: &str, left: &str, right: &str) -> String {
|
||||
let mut out = String::with_capacity(left.len() + s.len() + right.len());
|
||||
out.push_str(left);
|
||||
out.push_str(s);
|
||||
out.push_str(right);
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bold() {
|
||||
assert_eq!(bold(" foobar "), "* foobar *");
|
||||
assert_eq!(bold(" _foobar_ "), "* _foobar_ *");
|
||||
assert_eq!(bold("~(`foobar`)~"), "*~(`foobar`)~*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_italic() {
|
||||
assert_eq!(italic(" foobar "), "_ foobar _");
|
||||
assert_eq!(italic("*foobar*"), "_*foobar*_");
|
||||
assert_eq!(italic("~(foobar)~"), "_~(foobar)~_");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_underline() {
|
||||
assert_eq!(underline(" foobar "), "__ foobar __");
|
||||
assert_eq!(underline("*foobar*"), "__*foobar*__");
|
||||
assert_eq!(underline("~(foobar)~"), "__~(foobar)~__");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strike() {
|
||||
assert_eq!(strike(" foobar "), "~ foobar ~");
|
||||
assert_eq!(strike("*foobar*"), "~*foobar*~");
|
||||
assert_eq!(strike("*(foobar)*"), "~*(foobar)*~");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_italic_with_underline() {
|
||||
assert_eq!(underline(italic("foobar").as_str()), r"___foobar_\r__");
|
||||
assert_eq!(italic(underline("foobar").as_str()), r"___foobar_\r__");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_link() {
|
||||
assert_eq!(
|
||||
link("https://www.google.com/(`foobar`)", "google"),
|
||||
r"[google](https://www.google.com/(\`foobar\`\))",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_mention() {
|
||||
assert_eq!(
|
||||
user_mention(123456789, "pwner666"),
|
||||
"[pwner666](tg://user?id=123456789)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_block() {
|
||||
assert_eq!(
|
||||
code_block("pre-'formatted'\nfixed-width \\code `block`"),
|
||||
"```\npre-'formatted'\nfixed-width \\\\code \\`block\\`\n```"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_block_with_language() {
|
||||
assert_eq!(
|
||||
code_block_with_lang(
|
||||
"pre-'formatted'\nfixed-width \\code `block`",
|
||||
"python"
|
||||
),
|
||||
"```python\npre-'formatted'\nfixed-width \\\\code \\`block\\`\n```"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_inline() {
|
||||
assert_eq!(
|
||||
code_inline(" let x = (1, 2, 3); "),
|
||||
"` let x = (1, 2, 3); `"
|
||||
);
|
||||
assert_eq!(code_inline("<html>foo</html>"), "`<html>foo</html>`");
|
||||
assert_eq!(
|
||||
code_inline(r" `(code inside code \ )` "),
|
||||
r"` \`(code inside code \\ )\` `"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape() {
|
||||
assert_eq!(escape("* foobar *"), r"\* foobar \*");
|
||||
assert_eq!(
|
||||
escape(r"_ * [ ] ( ) ~ \ ` # + - = | { } . !"),
|
||||
r"\_ \* \[ \] \( \) \~ \ \` \# \+ \- \= \| \{ \} \. \!",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape_link_url() {
|
||||
assert_eq!(
|
||||
|
@ -55,14 +207,14 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_escapecode_block() {
|
||||
fn test_escape_code() {
|
||||
assert_eq!(
|
||||
escape_code_block(r"` \code inside the code\ `"),
|
||||
escape_code(r"` \code inside the code\ `"),
|
||||
r"\` \\code inside the code\\ \`"
|
||||
);
|
||||
assert_eq!(
|
||||
escape_code_block(r"_*[]()~\`#+-=|{}.!\"),
|
||||
r"_*[]()~\\\`#+-=|{}.!\\"
|
||||
escape_code(r"_*[]()~`#+-=|{}.!\"),
|
||||
r"_*[]()~\`#+-=|{}.!\\"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue