Merge pull request #18 from teloxide/impl_payload_macro

Add internal impl_payload macro
This commit is contained in:
Temirkhan Myrzamadi 2020-10-27 18:51:23 +06:00 committed by GitHub
commit 8a5081db61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 245 additions and 130 deletions

View file

@ -106,3 +106,204 @@ macro_rules! req_future {
};
}
/// Declares an item with a doc attribute computed by some macro expression.
/// This allows documentation to be dynamically generated based on input.
/// Necessary to work around https://github.com/rust-lang/rust/issues/52607.
#[macro_use]
macro_rules! calculated_doc {
(
$(
#[doc = $doc:expr]
$thing:item
)*
) => (
$(
#[doc = $doc]
$thing
)*
);
}
/// Declare payload type, implement `Payload` trait amd ::new method for it,
/// declare setters trait and implement it for all type which have payload.
#[macro_use]
macro_rules! impl_payload {
(
$(
#[ $($method_meta:tt)* ]
)*
$vi:vis $Method:ident ($Setters:ident) => $Ret:ty {
$(
required {
$(
$(
#[ $($field_meta:tt)* ]
)*
$v:vis $fields:ident : $FTy:ty $([$into:ident])?
,
)*
}
)?
$(
optional {
$(
$(
#[ $($opt_field_meta:tt)* ]
)*
$opt_v:vis $opt_fields:ident : $OptFTy:ty $([$opt_into:ident])?
),*
$(,)?
}
)?
}
) => {
$(
#[ $($method_meta)* ]
)*
$vi struct $Method {
$(
$(
$(
#[ $($field_meta)* ]
)*
$v $fields : $FTy,
)*
)?
$(
$(
$(
#[ $($opt_field_meta)* ]
)*
$opt_v $opt_fields : core::option::Option<$OptFTy>,
)*
)?
}
impl $Method {
$vi fn new($($($fields : impl_payload!(@into? $FTy $([$into])?)),*)?) -> Self {
Self {
$(
$(
$fields: $fields $(.$into())?,
)*
)?
$(
$(
$opt_fields: None,
)*
)?
}
}
}
impl $crate::requests::Payload for $Method {
type Output = $Ret;
const NAME: &'static str = stringify!($Method);
}
calculated_doc! {
#[doc = concat!(
"Setters for fields of [`",
stringify!($Method),
"`]"
)]
$vi trait $Setters: $crate::requests::HasPayload<Payload = $Method> + ::core::marker::Sized {
$(
$(
impl_payload! { @setter $Method $fields : $FTy $([$into])? }
)*
)?
$(
$(
impl_payload! { @setter_opt $Method $opt_fields : $OptFTy $([$opt_into])? }
)*
)?
}
}
impl<P> $Setters for P where P: crate::requests::HasPayload<Payload = $Method> {}
};
(@setter_opt $Method:ident $field:ident : $FTy:ty [into]) => {
calculated_doc! {
#[doc = concat!(
"Setter for [`",
stringify!($field),
"`](",
stringify!($Method),
"::",
stringify!($field),
") field."
)]
fn $field<T>(mut self, value: T) -> Self
where
T: Into<$FTy>,
{
self.payload_mut().$field = Some(value.into());
self
}
}
};
(@setter_opt $Method:ident $field:ident : $FTy:ty) => {
calculated_doc! {
#[doc = concat!(
"Setter for [`",
stringify!($field),
"`](",
stringify!($Method),
"::",
stringify!($field),
") field."
)]
fn $field(mut self, value: $FTy) -> Self {
self.payload_mut().$field = Some(value);
self
}
}
};
(@setter $Method:ident $field:ident : $FTy:ty [into]) => {
calculated_doc! {
#[doc = concat!(
"Setter for [`",
stringify!($field),
"`](",
stringify!($Method),
"::",
stringify!($field),
") field."
)]
fn $field<T>(mut self, value: T) -> Self
where
T: Into<$FTy>,
{
self.payload_mut().$field = value.into();
self
}
}
};
(@setter $Method:ident $field:ident : $FTy:ty) => {
calculated_doc! {
#[doc = concat!(
"Setter for [`",
stringify!($field),
"`](",
stringify!($Method),
"::",
stringify!($field),
") field."
)]
fn $field(mut self, value: $FTy) -> Self {
self.payload_mut().$field = value;
self
}
}
};
(@into? $T:ty [into]) => {
impl ::core::convert::Into<$T>
};
(@into? $T:ty) => {
$T
};
}

View file

@ -1,29 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::{
requests::{HasPayload, Payload},
types::User,
};
use crate::types::User;
/// A filter method for testing your bot's auth token. Requires no parameters.
/// Returns basic information about the bot in form of a [`User`] object.
///
/// [`User`]: crate::types::User
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default, Deserialize, Serialize)]
pub struct GetMe {}
impl GetMe {
pub const fn new() -> Self {
GetMe {}
}
impl_payload! {
/// A filter method for testing your bot's auth token. Requires no parameters.
/// Returns basic information about the bot in form of a [`User`] object.
///
/// [`User`]: crate::types::User
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default, Deserialize, Serialize)]
pub GetMe (GetMeSetters) => User {}
}
impl Payload for GetMe {
type Output = User;
const NAME: &'static str = "getMe";
}
pub trait GetMeSetters: HasPayload<Payload = GetMe> + Sized {}
impl<P> GetMeSetters for P where P: HasPayload<Payload = GetMe> {}

View file

@ -1,4 +1,5 @@
/// Payloads - data types sended to relegram
//! Payloads - data types sended to relegram
pub mod setters;
mod get_me;

View file

@ -1,112 +1,42 @@
use serde::{Deserialize, Serialize};
use crate::{
requests::{HasPayload, Payload},
types::{ChatId, Message, ParseMode, ReplyMarkup},
};
use crate::types::{ChatId, Message, ParseMode, ReplyMarkup};
/// Use this method to send text messages.
///
/// On success, the sent [`Message`] is returned.
///
/// [`Message`]: crate::types::Message
#[serde_with_macros::skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub struct SendMessage {
/// Unique identifier for the target chat or username of the target channel
/// (in the format `@channelusername`)
pub chat_id: ChatId,
/// Text of the message to be sent
pub text: String,
/// Send [Markdown] or [HTML], if you want Telegram apps to show
/// [bold, italic, fixed-width text or inline URLs] in your bot's message.
impl_payload! {
/// Use this method to send text messages.
///
/// [Markdown]: crate::types::ParseMode::Markdown
/// [HTML]: crate::types::ParseMode::HTML
/// [bold, italic, fixed-width text or inline URLs]:
/// crate::types::ParseMode
pub parse_mode: Option<ParseMode>,
/// Disables link previews for links in this message
pub disable_web_page_preview: Option<bool>,
/// Sends the message silently.
/// Users will receive a notification with no sound.
pub disable_notification: Option<bool>,
/// If the message is a reply, [id] of the original message
/// On success, the sent [`Message`] is returned.
///
/// [id]: crate::types::Message::id
pub reply_to_message_id: Option<i32>,
/// Additional interface options.
pub reply_markup: Option<ReplyMarkup>,
}
impl Payload for SendMessage {
type Output = Message;
const NAME: &'static str = "sendMessage";
}
impl SendMessage {
pub fn new<C, T>(chat_id: C, text: T) -> Self
where
C: Into<ChatId>,
T: Into<String>,
{
SendMessage {
chat_id: chat_id.into(),
text: text.into(),
parse_mode: None,
disable_web_page_preview: None,
disable_notification: None,
reply_to_message_id: None,
reply_markup: None,
/// [`Message`]: crate::types::Message
#[serde_with_macros::skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub SendMessage (SendMessageSetters) => Message {
required {
/// Unique identifier for the target chat or username of the target channel
/// (in the format `@channelusername`)
pub chat_id: ChatId [into],
/// Text of the message to be sent
pub text: String [into],
}
optional {
/// Send [Markdown] or [HTML], if you want Telegram apps to show
/// [bold, italic, fixed-width text or inline URLs] in your bot's message.
///
/// [Markdown]: crate::types::ParseMode::Markdown
/// [HTML]: crate::types::ParseMode::HTML
/// [bold, italic, fixed-width text or inline URLs]: crate::types::ParseMode
pub parse_mode: ParseMode,
/// Disables link previews for links in this message
pub disable_web_page_preview: bool,
/// Sends the message silently.
/// Users will receive a notification with no sound.
pub disable_notification: bool,
/// If the message is a reply, [id] of the original message
///
/// [id]: crate::types::Message::id
pub reply_to_message_id: i32,
/// Additional interface options.
pub reply_markup: ReplyMarkup,
}
}
}
pub trait SendMessageSetters: HasPayload<Payload = SendMessage> + Sized {
fn chat_id<T>(mut self, value: T) -> Self
where
T: Into<ChatId>,
{
self.payload_mut().chat_id = value.into();
self
}
fn text<T>(mut self, value: T) -> Self
where
T: Into<String>, // TODO: into?
{
self.payload_mut().text = value.into();
self
}
fn parse_mode(mut self, value: ParseMode) -> Self {
self.payload_mut().parse_mode = Some(value);
self
}
fn disable_web_page_preview(mut self, value: bool) -> Self {
self.payload_mut().disable_web_page_preview = Some(value);
self
}
fn disable_notification(mut self, value: bool) -> Self {
self.payload_mut().disable_notification = Some(value);
self
}
fn reply_to_message_id(mut self, value: i32) -> Self {
self.payload_mut().reply_to_message_id = Some(value);
self
}
fn reply_markup<T>(mut self, value: T) -> Self
where
T: Into<ReplyMarkup>,
{
self.payload_mut().reply_markup = Some(value.into());
self
}
}
impl<P> SendMessageSetters for P where P: HasPayload<Payload = SendMessage> {}