requests redesign part 0

This commit introduces 3 traits mentioned in requests redesign proposal:
- `Payload`
- `HasPayload`
- `Request`

Also it adds `Output<T>` alias to `<<T as HasPayload>::Payload as Payload>::Output`
This commit is contained in:
Waffle 2020-08-22 21:58:22 +03:00
parent 45a55c66de
commit 76d47276f6
5 changed files with 146 additions and 1 deletions

View file

@ -7,7 +7,8 @@
// ```console
// $ RUSTDOCFLAGS="--cfg docsrs" cargo doc --open --all-features
// ```
#![cfg_attr(all(docsrs, feature = "nightly"), feature(doc_cfg))]
#![cfg_attr(all(docsrs, feature = "nightly"), feature(doc_cfg, doc_spotlight))]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
#![forbid(unsafe_code)]
//#![deny(missing_docs)]

View file

@ -0,0 +1,45 @@
use crate::requests::Payload;
/// Represent types that have payload inside it. E.g.: the payload itself or a
/// `Request`.
///
/// This trait is something between [`DerefMut`] and [`BorrowMut`] — it allows
/// only one implementation per type (the [output] is associated type, not a
/// generic), have implementations for all types `P` such `P: `[`Payload`], but
/// have no magic compiler support like [`DerefMut`] does nor does it require
/// any laws about `Eq`, `Ord` and `Hash` as [`BorrowMut`] does.
///
/// Also [output] type is bounded by [`Payload`] trait.
///
/// This trait is mostly used to implement payload setters (on both payloads &
/// requests), so you probably won't find yourself using it directly.
///
/// [`DerefMut`]: std::ops::DerefMut
/// [`BorrowMut`]: std::borrow::BorrowMut
/// [`Payload`]: crate::requests::Payload
/// [output]: HasPayload::Payload
pub trait HasPayload {
/// Type of the payload contained.
type Payload: Payload;
/// Gain mutable access to the underlying payload.
fn payload_mut(&mut self) -> &mut Self::Payload;
/// Gain immutable access to the underlying payload.
fn payload_ref(&self) -> &Self::Payload;
}
impl<P> HasPayload for P
where
P: Payload,
{
type Payload = Self;
fn payload_mut(&mut self) -> &mut Self::Payload {
self
}
fn payload_ref(&self) -> &Self::Payload {
self
}
}

View file

@ -1,5 +1,11 @@
//! API requests.
mod has_payload;
mod payload;
mod request;
pub use self::{has_payload::HasPayload, payload::Payload, request::Request};
mod all;
mod utils;
@ -8,6 +14,10 @@ pub use all::*;
/// A type that is returned after making a request to Telegram.
pub type ResponseResult<T> = Result<T, crate::RequestError>;
/// Output of a [`Payload`] in [`HasPayload`]. Alias to
/// `<<T as HasPayload>::Payload as Payload>::Output`.
pub type Output<T> = <<T as HasPayload>::Payload as Payload>::Output;
/// Designates an API request.
#[async_trait::async_trait]
pub trait RequestOld {

20
src/requests/payload.rs Normal file
View file

@ -0,0 +1,20 @@
/// Payload of a request.
///
/// Simply speaking structs implementing this trait represent arguments of
/// a telegram bot API method.
///
/// This trait provides some additional information needed for sending request
/// to the telegram.
#[cfg_attr(all(docsrs, feature = "nightly"), doc(spotlight))]
pub trait Payload {
/// Return type of the telegram method.
///
/// Note: that should not include result wrappers (e.g. it should be simply
/// `Message`, `True` or something else)
type Output;
/// Name of the telegram method. Case insensitive, though must not include
/// underscores. (e.g.: `GetMe`, `GETME`, `getme`, `getMe` are ok, but
/// `get_me` is not ok)
const NAME: &'static str;
}

69
src/requests/request.rs Normal file
View file

@ -0,0 +1,69 @@
use std::future::Future;
use crate::requests::{HasPayload, Output};
/// A ready-to-send telegram request.
// FIXME(waffle): Write better doc for the trait
#[cfg_attr(all(docsrs, feature = "nightly"), doc(spotlight))]
pub trait Request: HasPayload {
/*
* Could be mostly `core::future::IntoFuture` though there is no reason to
* use it before it's integrated in async/await
*/
/// Type of error that may happen during sending the request to telegram.
type Err;
/// Type of future returned by [`send`](Request::send) method.
type Send: Future<Output = Result<Output<Self>, Self::Err>>;
/// Type of future returned by [`send_ref`](Request::send_ref) method.
///
/// NOTE: it intentionally forbids borrowing from self
// though anyway we couldn't allow borrowing without GATs :sob:
type SendRef: Future<Output = Result<Output<Self>, Self::Err>>;
/// Send the request.
///
/// ## Examples
// FIXME(waffle): ignored until full request redesign lands
/// ```ignore
/// # async {
/// use teloxide_core::{methods::GetMe, requests::{Request, RequestJson}, types::User, bot::Bot};
///
/// let bot = Bot::new("TOKEN");
/// let method = GetMe::new();
/// let request = JsonRequest::new(bot, method);
/// let _: User = request.send().await.unwrap();
/// # }
/// ```
fn send(self) -> Self::Send;
/// Send the request.
///
/// This method is analogous to [`send`](Request::send), but it doesn't take
/// the ownership of `self`. This allows to send the same (or slightly
/// different) requests over and over.
///
/// _Also_ it is expected that calling this method is better than just
/// `clone`ing the requests. (because instead of copying all the data
/// and then serializing it, this method should just serialize the data)
///
/// ## Examples
// FIXME(waffle): ignored until full request redisign lands
/// ```ignore
/// # async {
/// use teloxide_core::prelude::*;
///
/// let bot = Bot::new("TOKEN");
/// # let chat_ids = vec![1, 2, 3, 4].into_iter().map(Into::into);
///
/// let mut req = bot.send_message(0, "Hi there!");
/// for chat_id in chat_ids {
/// req.chat_id = chat_id;
/// req.send_ref().await.unwrap();
/// }
/// # }
/// ```
fn send_ref(&self) -> Self::SendRef;
}