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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use crate::utils::{
    result::{Result, TelegramError},
    FormDataFile,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{fs::File, path::Path};

/// struct for holding data needed to call
/// [`get_user_profile_photos`]
///
/// [`get_user_profile_photos`]:
/// ../../api/trait.API.html#method.get_user_profile_photos
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GetUserProfilePhotos {
    /// Unique identifier of the target user
    pub user_id: i64,
    /// Sequential number of the first photo to be returned. By default, all
    /// photos are returned.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub offset: Option<i64>,
    /// Limits the number of photos to be retrieved. Values between 1—100 are
    /// accepted. Defaults to 100.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<i64>,
}

/// struct for holding data needed to call
/// [`get_file`]
///
/// [`get_file`]:
/// ../../api/trait.API.html#method.get_file
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GetFile {
    /// File identifier to get info about
    pub file_id: String,
}

/// struct for holding data needed to call
/// [`answer_callback_query`]
///
/// [`answer_callback_query`]:
/// ../../api/trait.API.html#method.answer_callback_query
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AnswerCallbackQuery {
    /// Unique identifier for the query to be answered
    pub callback_query_id: String,
    /// Text of the notification. If not specified, nothing will be shown to the
    /// user, 0-200 characters
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    /// If true, an alert will be shown by the client instead of a notification
    /// at the top of the chat screen. Defaults to false.
    pub show_alert: bool,
    /// URL that will be opened by the user's client. If you have created a Game
    /// and accepted the conditions via @Botfather, specify the URL that
    /// opens your game – note that this will only work if the query comes from
    /// a callback_game button.
    ///
    /// Otherwise, you may use links like t.me/your_bot?start=XXXX that open
    /// your bot with a parameter.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
    /// The maximum amount of time in seconds that the result of the callback
    /// query may be cached client-side. Telegram apps will support caching
    /// starting in version 3.14. Defaults to 0.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cache_time: Option<i64>,
}

/// Is either true (the bool), or is object T
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum TrueOrObject<T> {
    True(bool),
    #[serde(bound(deserialize = "T: Deserialize<'de>"))]
    Object(T),
}

/// This object represents either the `file_id`, http url or the contents of a
/// file to be uploaded.
#[derive(Debug, Clone, PartialEq)]
pub enum InputFile {
    String(String),
    File(FormDataFile),
}

impl InputFile {
    pub fn new_file(mut file: &mut File, file_name: &str) -> Result<Self> {
        Ok(Self::File(FormDataFile::new_from_file(
            &mut file, file_name,
        )?))
    }

    pub fn new(string: &str) -> Self {
        Self::String(string.to_owned())
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
        let mut file = File::open(&path)?;
        let file_name = path.as_ref().file_name().ok_or_else(|| {
            TelegramError::InvalidArgument("file doesn't have a valid file name".to_owned())
        })?;

        Self::new_file(
            &mut file,
            file_name.to_str().ok_or_else(|| {
                TelegramError::InvalidArgument("file doesn't have a valid file name".to_owned())
            })?,
        )
    }
}

impl From<String> for InputFile {
    fn from(string: String) -> Self {
        Self::String(string)
    }
}

impl From<&str> for InputFile {
    fn from(string: &str) -> Self {
        Self::String(string.to_owned())
    }
}

impl From<FormDataFile> for InputFile {
    fn from(file: FormDataFile) -> Self {
        Self::File(file)
    }
}

impl Serialize for InputFile {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::String(ref c) => serializer.serialize_str(c),
            Self::File(ref c) => serializer.serialize_str(&format!(
                "attach://{}",
                &c.file_name
                    .as_ref()
                    .ok_or_else(|| serde::ser::Error::custom(
                        "file name doesn't exist for the InputFile file"
                    ))?
            )),
        }
    }
}

impl<'de> Deserialize<'de> for InputFile {
    fn deserialize<D>(deserializer: D) -> std::result::Result<InputFile, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(Self::String(Deserialize::deserialize(deserializer)?))
    }
}