Merge pull request #135 from teloxide/checked_types

Checked types
This commit is contained in:
Temirkhan Myrzamadi 2020-01-12 02:35:03 +06:00 committed by GitHub
commit 7e02337ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 640 additions and 28 deletions

View file

@ -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"
either = "1.5.3"
mime = "0.3.16"

View file

@ -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.

View file

@ -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,
}
}
}))
}

View file

@ -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();

View file

@ -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;

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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 =

View file

@ -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,

View file

@ -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 =

View file

@ -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,

View file

@ -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)

View file

@ -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&amp;l=ru\">&lt;google&gt;</a>",
"<a href=\"https://www.google.com/?q=foo&amp;l=ru\">&lt;google&gt;\
</a>",
);
}