tg_flows/types/parse_mode.rs
1// see https://github.com/rust-lang/rust/issues/38832
2// (for built ins there no warnings, but for (De)Serialize, there are)
3#![allow(deprecated)]
4
5use std::{
6 convert::{TryFrom, TryInto},
7 str::FromStr,
8};
9
10use serde::{Deserialize, Serialize};
11
12/// Formatting options.
13///
14/// The Bot API supports basic formatting for messages. You can use bold,
15/// italic, underlined, strikethrough, and spoiler text, as well as inline links
16/// and pre-formatted code in your bots' messages. Telegram clients will render
17/// them accordingly. You can use either markdown-style or HTML-style
18/// formatting.
19///
20/// Note that Telegram clients will display an **alert** to the user before
21/// opening an inline link ('Open this link?' together with the full URL).
22///
23/// Message entities can be nested, providing following restrictions are met:
24/// - If two entities have common characters then one of them is fully contained
25/// inside another.
26/// - bold, italic, underline, strikethrough, and spoiler entities can contain
27/// and can be part of any other entities, except pre and code.
28/// - All other entities can't contain each other.
29///
30/// Links `tg://user?id=<user_id>` can be used to mention a user by their ID
31/// without using a username. Please note:
32///
33/// - These links will work **only** if they are used inside an inline link. For
34/// example, they will not work, when used in an inline keyboard button or in
35/// a message text.
36/// - These mentions are only guaranteed to work if the user has contacted the
37/// bot in the past, has sent a callback query to the bot via inline button or
38/// is a member in the group where he was mentioned.
39///
40/// ## MarkdownV2 style
41///
42/// To use this mode, pass [`MarkdownV2`] in the `parse_mode` field.
43/// Use the following syntax in your message:
44/// ````text
45/// *bold \*text*
46/// _italic \*text_
47/// __underline__
48/// ~strikethrough~
49/// ||spoiler||
50/// *bold _italic bold ~italic bold strikethrough ||italic bold strikethrough spoiler||~ __underline italic bold___ bold*
51/// [inline URL](http://www.example.com/)
52/// [inline mention of a user](tg://user?id=123456789)
53/// `inline fixed-width code`
54/// ```
55/// pre-formatted fixed-width code block
56/// ```
57/// ```rust
58#[doc = "pre-formatted fixed-width code block written in the Rust programming language"]
59/// ```
60/// ````
61///
62/// Please note:
63/// - Any character between 1 and 126 inclusively can be escaped anywhere with a
64/// preceding '\' character, in which case it is treated as an ordinary
65/// character and not a part of the markup.
66/// - Inside `pre` and `code` entities, all '`‘ and ’\‘ characters must be
67/// escaped with a preceding ’\' character.
68/// - Inside `(...)` part of inline link definition, all ')‘ and ’\‘ must be
69/// escaped with a preceding ’\' character.
70/// - In all other places characters ’_‘, ’*‘, ’[‘, ’]‘, ’(‘, ’)‘, ’~‘, ’`‘,
71/// ’>‘, ’#‘, ’+‘, ’+‘, ’-‘, ’|‘, ’{‘, ’}‘, ’.‘, ’!‘ must be escaped with the
72/// preceding character ’\'.
73/// - In case of ambiguity between `italic` and `underline` entities ‘__’ is
74/// always greadily treated from left to right as beginning or end of
75/// `underline` entity, so instead of `___italic underline___` use `___italic
76/// underline_\r__`, where `\r` is a character with code `13`, which will be
77/// ignored.
78///
79/// ## HTML style
80///
81/// To use this mode, pass [`Html`] in the `parse_mode` field.
82/// The following tags are currently supported:
83/// ````text
84/// <b>bold</b>, <strong>bold</strong>
85/// <i>italic</i>, <em>italic</em>
86/// <u>underline</u>, <ins>underline</ins>
87/// <s>strikethrough</s>, <strike>strikethrough</strike>, <del>strikethrough</del>
88/// <span class="tg-spoiler">spoiler</span>, <tg-spoiler>spoiler</tg-spoiler>
89/// <b>bold <i>italic bold <s>italic bold strikethrough <span class="tg-spoiler">italic bold strikethrough spoiler</span></s> <u>underline italic bold</u></i> bold</b>
90/// <a href="http://www.example.com/">inline URL</a>
91/// <a href="tg://user?id=123456789">inline mention of a user</a>
92/// <code>inline fixed-width code</code>
93/// <pre>pre-formatted fixed-width code block</pre>
94#[doc = "<pre><code class=\"language-rust\">pre-formatted fixed-width code block written in the \
95 Rust programming language</code></pre>"]
96/// ````
97///
98/// Please note:
99///
100/// - Only the tags mentioned above are currently supported.
101/// - All `<`, `>` and `&` symbols that are not a part of a tag or an HTML
102/// entity must be replaced with the corresponding HTML entities (`<` with
103/// `<`, `>` with `>` and `&` with `&`).
104/// - All numerical HTML entities are supported.
105/// - The API currently supports only the following named HTML entities: `<`,
106/// `>`, `&` and `"`.
107/// - Use nested `pre` and `code` tags, to define programming language for `pre`
108/// entity.
109/// - Programming language can't be specified for standalone `code` tags.
110///
111/// ## Markdown style
112///
113/// This is a legacy mode, retained for backward compatibility. To use this
114/// mode, pass [`Markdown`] in the `parse_mode` field.
115/// Use the following syntax in your message:
116/// ````text
117/// *bold text*
118/// _italic text_
119/// [inline URL](http://www.example.com/)
120/// [inline mention of a user](tg://user?id=123456789)
121/// `inline fixed-width code`
122/// ```
123/// pre-formatted fixed-width code block
124/// ```
125/// ```rust
126/// pre-formatted fixed-width code block written in the Rust programming language
127/// ```
128/// ````
129///
130/// Please note:
131/// - Entities must not be nested, use parse mode [`MarkdownV2`] instead.
132/// - There is no way to specify underline and strikethrough entities, use parse
133/// mode [`MarkdownV2`] instead.
134/// - To escape characters ’_‘, ’*‘, ’`‘, ’[‘ outside of an entity, prepend the
135/// characters ’\' before them.
136/// - Escaping inside entities is not allowed, so entity must be closed first
137/// and reopened again: use `_snake_\__case_` for italic `snake_case` and
138/// `*2*\**2=4*` for bold `2*2=4`.
139///
140/// [`MarkdownV2`]: ParseMode::MarkdownV2
141/// [`Html`]: ParseMode::Html
142/// [`Markdown`]: ParseMode::Markdown
143#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
144pub enum ParseMode {
145 MarkdownV2,
146 #[serde(rename = "HTML")]
147 Html,
148 #[deprecated = "This is a legacy mode, retained for backward compatibility. Use `MarkdownV2` \
149 instead."]
150 Markdown,
151}
152
153impl TryFrom<&str> for ParseMode {
154 type Error = ();
155
156 fn try_from(value: &str) -> Result<Self, Self::Error> {
157 let normalized = value.to_lowercase();
158 match normalized.as_ref() {
159 "html" => Ok(ParseMode::Html),
160 "markdown" => Ok(ParseMode::Markdown),
161 "markdownv2" => Ok(ParseMode::MarkdownV2),
162 _ => Err(()),
163 }
164 }
165}
166
167impl TryFrom<String> for ParseMode {
168 type Error = ();
169
170 fn try_from(value: String) -> Result<Self, Self::Error> {
171 value.as_str().try_into()
172 }
173}
174
175impl FromStr for ParseMode {
176 type Err = ();
177
178 fn from_str(s: &str) -> Result<Self, Self::Err> {
179 s.try_into()
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 #![allow(deprecated)]
186
187 use super::*;
188
189 #[test]
190 fn html_serialization() {
191 let expected_json = String::from(r#""HTML""#);
192 let actual_json = serde_json::to_string(&ParseMode::Html).unwrap();
193
194 assert_eq!(expected_json, actual_json)
195 }
196
197 #[test]
198 fn markdown_serialization() {
199 let expected_json = String::from(r#""Markdown""#);
200 let actual_json = serde_json::to_string(&ParseMode::Markdown).unwrap();
201
202 assert_eq!(expected_json, actual_json)
203 }
204}