tbot/markup/
link.rs

1use super::{html, markdown_v2, Formattable, Nesting};
2use crate::types::user;
3use std::{
4    fmt::{self, Formatter, Write},
5    ops::Deref,
6};
7
8#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
9enum Kind<T> {
10    Link(T),
11    Mention(user::Id),
12}
13
14/// Formats a link. Can be created with [`link`] or [`mention`].
15///
16/// [`link`]: ./fn.link.html
17/// [`mention`]: ./fn.mention.html
18#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
19#[must_use = "formatters need to be formatted with `markdown_v2` or `html`"]
20pub struct Link<T, L = &'static str> {
21    text: T,
22    link: Kind<L>,
23}
24
25/// Creates a link.
26pub fn link<T, L>(text: T, link: L) -> Link<T, L>
27where
28    T: Formattable,
29    L: Deref<Target = str>,
30{
31    Link {
32        text,
33        link: Kind::Link(link),
34    }
35}
36
37/// Creates a mention by ID.
38pub fn mention<T: Formattable>(text: T, user: user::Id) -> Link<T> {
39    Link {
40        text,
41        link: Kind::Mention(user),
42    }
43}
44
45impl<T, L> markdown_v2::Formattable for Link<T, L>
46where
47    T: Formattable,
48    L: Deref<Target = str>,
49{
50    fn format(
51        &self,
52        formatter: &mut Formatter,
53        nesting: Nesting,
54    ) -> fmt::Result {
55        formatter.write_char('[')?;
56        markdown_v2::Formattable::format(&self.text, formatter, nesting)?;
57        formatter.write_str("](")?;
58
59        match &self.link {
60            Kind::Link(link) => link
61                .deref()
62                .chars()
63                .map(|x| {
64                    if markdown_v2::ESCAPED_LINK_CHARACTERS.contains(&x) {
65                        formatter.write_char('\\')?;
66                    }
67                    formatter.write_char(x)
68                })
69                .collect::<Result<(), _>>()?,
70            Kind::Mention(user::Id(id)) => {
71                write!(formatter, "tg://user?id={}", id)?
72            }
73        }
74        formatter.write_char(')')
75    }
76}
77
78impl<T, L> html::Formattable for Link<T, L>
79where
80    T: Formattable,
81    L: Deref<Target = str>,
82{
83    fn format(
84        &self,
85        formatter: &mut Formatter,
86        nesting: Nesting,
87    ) -> fmt::Result {
88        formatter.write_str("<a href=\"")?;
89
90        match &self.link {
91            Kind::Link(link) => link
92                .deref()
93                .chars()
94                .map(|x| {
95                    if x == '"' {
96                        formatter.write_char('\\')?;
97                    }
98                    formatter.write_char(x)
99                })
100                .collect::<Result<(), _>>()?,
101            Kind::Mention(user::Id(id)) => {
102                write!(formatter, "tg://user?id={}", id)?
103            }
104        }
105
106        formatter.write_str("\">")?;
107        html::Formattable::format(&self.text, formatter, nesting)?;
108        formatter.write_str("</a>")
109    }
110}