diff --git a/Cargo.toml b/Cargo.toml index c4ac9602..d921bb1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,5 @@ async-trait = "0.1.22" futures = "0.3.1" pin-project = "0.4.6" serde_with_macros = "1.0.1" -either = "1.5.3" \ No newline at end of file +either = "1.5.3" +mime = "0.3.16" \ No newline at end of file diff --git a/src/dispatching/dispatchers/filter.rs b/src/dispatching/dispatchers/filter.rs index 7c993a1a..5124d6a3 100644 --- a/src/dispatching/dispatchers/filter.rs +++ b/src/dispatching/dispatchers/filter.rs @@ -31,10 +31,11 @@ type FiltersWithHandlers<'a, T, E> = Vec<FilterWithHandler<'a, T, E>>; /// use std::convert::Infallible; /// use teloxide::{dispatching::FilterDispatcher, RequestError}; /// -/// let _ = -/// FilterDispatcher::new(|err: Either<RequestError, Infallible>| async { +/// let _ = FilterDispatcher::new(|err: Either<RequestError, Infallible>| { +/// async { /// dbg!(err); -/// }); +/// } +/// }); /// ``` /// /// Or you can do it even simpler by providing the built-in error handler @@ -83,9 +84,11 @@ type FiltersWithHandlers<'a, T, E> = Vec<FilterWithHandler<'a, T, E>>; /// // error handler that just ignores all errors (that can't ever happen). /// let mut dp = FilterDispatcher::<Infallible, _>::new(|_| async {}) /// // Add a handler, which handles all messages sent to the bot. -/// .message_handler(true, |mes: Message| async move { -/// println!("New message: {:?}", mes); -/// Ok(()) +/// .message_handler(true, |mes: Message| { +/// async move { +/// println!("New message: {:?}", mes); +/// Ok(()) +/// } /// }) /// // Add a handler, which handles all messages edited in a chat /// // with the bot. diff --git a/src/network/download.rs b/src/network/download.rs index 05f08f5d..0e68414f 100644 --- a/src/network/download.rs +++ b/src/network/download.rs @@ -39,11 +39,13 @@ pub async fn download_file_stream( .await? .error_for_status()?; - Ok(futures::stream::unfold(res, |mut res| async { - match res.chunk().await { - Err(err) => Some((Err(err), res)), - Ok(Some(c)) => Some((Ok(c), res)), - Ok(None) => None, + Ok(futures::stream::unfold(res, |mut res| { + async { + match res.chunk().await { + Err(err) => Some((Err(err), res)), + Ok(Some(c)) => Some((Ok(c), res)), + Ok(None) => None, + } } })) } diff --git a/src/types/animation.rs b/src/types/animation.rs index f589c589..92d7e9e6 100644 --- a/src/types/animation.rs +++ b/src/types/animation.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::types::PhotoSize; +use crate::types::{MimeWrapper, PhotoSize}; /// This object represents an animation file (GIF or H.264/MPEG-4 AVC video /// without sound). @@ -33,7 +33,7 @@ pub struct Animation { pub file_name: Option<String>, /// A MIME type of the file as defined by a sender. - pub mime_type: Option<String>, + pub mime_type: Option<MimeWrapper>, /// A size of a file. pub file_size: Option<u32>, @@ -59,7 +59,7 @@ mod tests { "file_size":3452 }, "file_name":"some", - "mime_type":"gif", + "mime_type":"video/gif", "file_size":6500}"#; let expected = Animation { file_id: "id".to_string(), @@ -75,7 +75,9 @@ mod tests { file_size: Some(3452), }), file_name: Some("some".to_string()), - mime_type: Some("gif".to_string()), + mime_type: Some(MimeWrapper { + mime: "video/gif".parse().unwrap(), + }), file_size: Some(6500), }; let actual = serde_json::from_str::<Animation>(json).unwrap(); diff --git a/src/types/mod.rs b/src/types/mod.rs index 651271e5..8baca877 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -171,3 +171,6 @@ mod encrypted_credentials; mod encrypted_passport_element; mod passport_data; mod passport_file; + +pub use non_telegram_types::*; +mod non_telegram_types; diff --git a/src/types/non_telegram_types/country_code.rs b/src/types/non_telegram_types/country_code.rs new file mode 100644 index 00000000..4f7705ac --- /dev/null +++ b/src/types/non_telegram_types/country_code.rs @@ -0,0 +1,254 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum CountryCode { + AD, + AE, + AF, + AG, + AI, + AL, + AM, + AO, + AQ, + AR, + AS, + AT, + AU, + AW, + AX, + AZ, + BA, + BB, + BD, + BE, + BF, + BG, + BH, + BI, + BJ, + BL, + BM, + BN, + BO, + BQ, + BR, + BS, + BT, + BV, + BW, + BY, + BZ, + CA, + CC, + CD, + CF, + CG, + CH, + CI, + CK, + CL, + CM, + CN, + CO, + CR, + CU, + CV, + CW, + CX, + CY, + CZ, + DE, + DJ, + DK, + DM, + DO, + DZ, + EC, + EE, + EG, + EH, + ER, + ES, + ET, + FI, + FJ, + FK, + FM, + FO, + FR, + GA, + GB, + GD, + GE, + GF, + GG, + GH, + GI, + GL, + GM, + GN, + GP, + GQ, + GR, + GS, + GT, + GU, + GW, + GY, + HK, + HM, + HN, + HR, + HT, + HU, + ID, + IE, + IL, + IM, + IN, + IO, + IQ, + IR, + IS, + IT, + JE, + JM, + JO, + JP, + KE, + KG, + KH, + KI, + KM, + KN, + KP, + KR, + KW, + KY, + KZ, + LA, + LB, + LC, + LI, + LK, + LR, + LS, + LT, + LU, + LV, + LY, + MA, + MC, + MD, + ME, + MF, + MG, + MH, + MK, + ML, + MM, + MN, + MO, + MP, + MQ, + MR, + MS, + MT, + MU, + MV, + MW, + MX, + MY, + MZ, + NA, + NC, + NE, + NF, + NG, + NI, + NL, + NO, + NP, + NR, + NU, + NZ, + OM, + PA, + PE, + PF, + PG, + PH, + PK, + PL, + PM, + PN, + PR, + PS, + PT, + PW, + PY, + QA, + RE, + RO, + RS, + RU, + RW, + SA, + SB, + SC, + SD, + SE, + SG, + SH, + SI, + SJ, + SK, + SL, + SM, + SN, + SO, + SR, + SS, + ST, + SV, + SX, + SY, + SZ, + TC, + TD, + TF, + TG, + TH, + TJ, + TK, + TL, + TM, + TN, + TO, + TR, + TT, + TV, + TW, + TZ, + UA, + UG, + UM, + US, + UY, + UZ, + VA, + VC, + VE, + VG, + VI, + VN, + VU, + WF, + WS, + YE, + YT, + ZA, + ZM, + ZW, +} diff --git a/src/types/non_telegram_types/currency.rs b/src/types/non_telegram_types/currency.rs new file mode 100644 index 00000000..0cdebce6 --- /dev/null +++ b/src/types/non_telegram_types/currency.rs @@ -0,0 +1,89 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum Currency { + AED, + AFN, + ALL, + AMD, + ARS, + AUD, + AZN, + BAM, + BDT, + BGN, + BND, + BOB, + BRL, + CAD, + CHF, + CLP, + CNY, + COP, + CRC, + CZK, + DKK, + DOP, + DZD, + EGP, + EUR, + GBP, + GEL, + GTQ, + HKD, + HNL, + HRK, + HUF, + IDR, + ILS, + INR, + ISK, + JMD, + JPY, + KES, + KGS, + KRW, + KZT, + LBP, + LKR, + MAD, + MDL, + MNT, + MUR, + MVR, + MXN, + MYR, + MZN, + NGN, + NIO, + NOK, + NPR, + NZD, + PAB, + PEN, + PHP, + PKR, + PLN, + PYG, + QAR, + RON, + RSD, + RUB, + SAR, + SEK, + SGD, + THB, + TJS, + TRY, + TTD, + TWD, + TZS, + UAH, + UGX, + USD, + UYU, + UZS, + VND, + YER, + ZAR, +} diff --git a/src/types/non_telegram_types/language_code.rs b/src/types/non_telegram_types/language_code.rs new file mode 100644 index 00000000..1837db6e --- /dev/null +++ b/src/types/non_telegram_types/language_code.rs @@ -0,0 +1,190 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum LanguageCode { + AA, + AB, + AE, + AF, + AK, + AM, + AN, + AR, + AS, + AV, + AY, + AZ, + BA, + BE, + BG, + BH, + BI, + BM, + BN, + BO, + BR, + BS, + CA, + CE, + CH, + CO, + CR, + CS, + CU, + CV, + CY, + DA, + DE, + DV, + DZ, + EE, + EL, + EN, + EO, + ES, + ET, + EU, + FA, + FF, + FI, + FJ, + FO, + FR, + FY, + GA, + GD, + GL, + GN, + GU, + GV, + HA, + HE, + HI, + HO, + HR, + HT, + HU, + HY, + HZ, + IA, + ID, + IE, + IG, + II, + IK, + IO, + IS, + IT, + IU, + JA, + JV, + KA, + KG, + KI, + KJ, + KK, + KL, + KM, + KN, + KO, + KR, + KS, + KU, + KV, + KW, + KY, + LA, + LB, + LG, + LI, + LN, + LO, + LT, + LU, + LV, + MG, + MH, + MI, + MK, + ML, + MN, + MR, + MS, + MT, + MY, + NA, + NB, + ND, + NE, + NG, + NL, + NN, + NO, + NR, + NV, + NY, + OC, + OJ, + OM, + OR, + OS, + PA, + PI, + PL, + PS, + PT, + QU, + RM, + RN, + RO, + RU, + RW, + SA, + SC, + SD, + SE, + SG, + SI, + SK, + SL, + SM, + SN, + SO, + SQ, + SR, + SS, + ST, + SU, + SV, + SW, + TA, + TE, + TG, + TH, + TI, + TK, + TL, + TN, + TO, + TR, + TS, + TT, + TW, + TY, + UG, + UK, + UR, + UZ, + VE, + VI, + VO, + WA, + WO, + XH, + YI, + YO, + ZA, + ZH, + ZU, +} diff --git a/src/types/non_telegram_types/mime_wrapper.rs b/src/types/non_telegram_types/mime_wrapper.rs new file mode 100644 index 00000000..20222e1c --- /dev/null +++ b/src/types/non_telegram_types/mime_wrapper.rs @@ -0,0 +1,56 @@ +use derive_more::From; +use mime::Mime; +use serde::{ + de::Visitor, export::Formatter, Deserialize, Deserializer, Serialize, + Serializer, +}; + +#[derive(Clone, Debug, Eq, Hash, PartialEq, From)] +pub struct MimeWrapper { + pub mime: Mime, +} + +impl Serialize for MimeWrapper { + fn serialize<S>( + &self, + serializer: S, + ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> + where + S: Serializer, + { + serializer.serialize_str(self.mime.as_ref()) + } +} + +struct MimeVisitor; +impl<'a> Visitor<'a> for MimeVisitor { + type Value = MimeWrapper; + + fn expecting( + &self, + formatter: &mut Formatter<'_>, + ) -> Result<(), serde::export::fmt::Error> { + formatter.write_str("mime type") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + match v.parse::<Mime>() { + Ok(mime_type) => Ok(MimeWrapper { mime: mime_type }), + Err(e) => Err(E::custom(e)), + } + } +} + +impl<'de> Deserialize<'de> for MimeWrapper { + fn deserialize<D>( + deserializer: D, + ) -> Result<Self, <D as Deserializer<'de>>::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(MimeVisitor) + } +} diff --git a/src/types/non_telegram_types/mod.rs b/src/types/non_telegram_types/mod.rs new file mode 100644 index 00000000..2202122b --- /dev/null +++ b/src/types/non_telegram_types/mod.rs @@ -0,0 +1,9 @@ +pub use country_code::*; +pub use currency::*; +pub use language_code::*; +pub use mime_wrapper::*; + +mod country_code; +mod currency; +mod language_code; +mod mime_wrapper; diff --git a/src/types/pre_checkout_query.rs b/src/types/pre_checkout_query.rs index ef668553..172e5e8f 100644 --- a/src/types/pre_checkout_query.rs +++ b/src/types/pre_checkout_query.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::types::{OrderInfo, User}; +use crate::types::{Currency, OrderInfo, User}; /// This object contains information about an incoming pre-checkout query. /// @@ -17,7 +17,7 @@ pub struct PreCheckoutQuery { /// Three-letter ISO 4217 [currency] code. /// /// [currency]: https://core.telegram.org/bots/payments#supported-currencies - pub currency: String, + pub currency: Currency, /// Total price in the _smallest units_ of the currency (integer, **not** /// float/double). For example, for a price of `US$ 1.45` pass `amount = diff --git a/src/types/shipping_address.rs b/src/types/shipping_address.rs index b323bb24..478ad714 100644 --- a/src/types/shipping_address.rs +++ b/src/types/shipping_address.rs @@ -1,3 +1,4 @@ +use crate::types::CountryCode; use serde::{Deserialize, Serialize}; /// This object represents a shipping address. @@ -6,7 +7,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct ShippingAddress { /// ISO 3166-1 alpha-2 country code. - pub country_code: String, + pub country_code: CountryCode, /// State, if applicable. pub state: String, diff --git a/src/types/successful_payment.rs b/src/types/successful_payment.rs index a3508bd7..d38a36b5 100644 --- a/src/types/successful_payment.rs +++ b/src/types/successful_payment.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::types::OrderInfo; +use crate::types::{Currency, OrderInfo}; /// This object contains basic information about a successful payment. /// @@ -11,7 +11,7 @@ pub struct SuccessfulPayment { /// Three-letter ISO 4217 [currency] code. /// /// [currency]: https://core.telegram.org/bots/payments#supported-currencies - pub currency: String, + pub currency: Currency, /// Total price in the smallest units of the currency (integer, not /// float/double). For example, for a price of `US$ 1.45` pass `amount = diff --git a/src/types/update.rs b/src/types/update.rs index 2777ebd2..3dfb2270 100644 --- a/src/types/update.rs +++ b/src/types/update.rs @@ -63,8 +63,8 @@ pub enum UpdateKind { #[cfg(test)] mod test { use crate::types::{ - Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind, Sender, - Update, UpdateKind, User, + Chat, ChatKind, ForwardKind, LanguageCode, MediaKind, Message, + MessageKind, Sender, Update, UpdateKind, User, }; // TODO: more tests for deserialization @@ -114,7 +114,7 @@ mod test { first_name: String::from("Waffle"), last_name: None, username: Some(String::from("WaffleLapkin")), - language_code: Some(String::from("en")), + language_code: Some(LanguageCode::EN), }), forward_kind: ForwardKind::Origin { reply_to_message: None, diff --git a/src/types/user.rs b/src/types/user.rs index 52bb3b25..67086ca6 100644 --- a/src/types/user.rs +++ b/src/types/user.rs @@ -1,3 +1,4 @@ +use crate::types::LanguageCode; use serde::{Deserialize, Serialize}; /// This object represents a Telegram user or bot. @@ -24,7 +25,7 @@ pub struct User { /// [IETF language tag] of the user's language. /// /// [IETF language tag]: https://en.wikipedia.org/wiki/IETF_language_tag - pub language_code: Option<String>, + pub language_code: Option<LanguageCode>, } #[cfg(test)] @@ -39,7 +40,7 @@ mod tests { "first_name":"firstName", "last_name":"lastName", "username":"Username", - "language_code":"languageCode" + "language_code":"ru" }"#; let expected = User { id: 12345, @@ -47,7 +48,7 @@ mod tests { first_name: "firstName".to_string(), last_name: Some("lastName".to_string()), username: Some("Username".to_string()), - language_code: Some("languageCode".to_string()), + language_code: Some(LanguageCode::RU), }; let actual = serde_json::from_str::<User>(&json).unwrap(); assert_eq!(actual, expected) diff --git a/src/utils/html.rs b/src/utils/html.rs index a4f5a7d9..dbd91cfa 100644 --- a/src/utils/html.rs +++ b/src/utils/html.rs @@ -121,7 +121,8 @@ mod tests { fn test_link() { assert_eq!( link("https://www.google.com/?q=foo&l=ru", "<google>"), - "<a href=\"https://www.google.com/?q=foo&l=ru\"><google></a>", + "<a href=\"https://www.google.com/?q=foo&l=ru\"><google>\ + </a>", ); }