1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::utils::text_decorations::{add_surrogates, remove_surrogates};

use super::User;

use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;

/// This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
/// # Documentation
/// <https://core.telegram.org/bots/api#messageentity>
#[skip_serializing_none]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct MessageEntity {
    /// Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag`), 'cashtag' (:code:`$USD`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users [`without usernames`](https://telegram.org/blog/edit#new-mentions)), 'custom_emoji' (for inline custom emoji stickers)
    #[serde(rename = "type")]
    pub entity_type: String,
    /// Offset in UTF-16 code units to the start of the entity
    pub offset: i64,
    /// Length of the entity in UTF-16 code units
    pub length: i64,
    /// For 'text_link' only, URL that will be opened after user taps on the text
    pub url: Option<String>,
    /// For 'text_mention' only, the mentioned user
    pub user: Option<User>,
    /// For 'pre' only, the programming language of the entity text
    pub language: Option<String>,
    /// For 'custom_emoji' only, unique identifier of the custom emoji. Use [`GetCustomEmojiStickers`](crate::methods::GetCustomEmojiStickers) to get full information about the sticker
    pub custom_emoji_id: Option<String>,
}

impl MessageEntity {
    #[must_use]
    pub fn new(entity_type: impl Into<String>, offset: i64, length: i64) -> Self {
        Self {
            entity_type: entity_type.into(),
            offset,
            length,
            url: None,
            user: None,
            language: None,
            custom_emoji_id: None,
        }
    }

    #[must_use]
    pub fn offset(self, val: i64) -> Self {
        Self {
            offset: val,
            ..self
        }
    }

    #[must_use]
    pub fn length(self, val: i64) -> Self {
        Self {
            length: val,
            ..self
        }
    }

    #[must_use]
    pub fn url(self, val: impl Into<String>) -> Self {
        Self {
            url: Some(val.into()),
            ..self
        }
    }

    #[must_use]
    pub fn user(self, val: User) -> Self {
        Self {
            user: Some(val),
            ..self
        }
    }

    #[must_use]
    pub fn language(self, val: impl Into<String>) -> Self {
        Self {
            language: Some(val.into()),
            ..self
        }
    }

    #[must_use]
    pub fn custom_emoji_id(self, val: impl Into<String>) -> Self {
        Self {
            custom_emoji_id: Some(val.into()),
            ..self
        }
    }
}

impl MessageEntity {
    /// # Panics
    /// If the `self.offset` or `self.offset + self.length` is out of the range
    #[must_use]
    pub fn extract_from(&self, text: &str) -> String {
        let with_surrogates = add_surrogates(
            &text[usize::try_from(self.offset).unwrap() * 2
                ..usize::try_from(self.offset + self.length).unwrap() * 2],
        );

        remove_surrogates(&with_surrogates)
    }
}