mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-11 04:21:12 +01:00
Merge pull request #8 from teloxide/requests_redisign_p3
Implement auto sending requests
This commit is contained in:
commit
745d4a1ffe
5 changed files with 208 additions and 3 deletions
191
src/bot/auto_send.rs
Normal file
191
src/bot/auto_send.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures::future::FusedFuture;
|
||||
|
||||
use crate::requests::{HasPayload, Output, Request, Requester};
|
||||
|
||||
/// Send requests automatically.
|
||||
///
|
||||
/// Requests returned by `<AutoSend<_> as `[`Requester`]`>` are [`Future`]s
|
||||
/// which means that you can simply `.await` them instead of using
|
||||
/// `.send().await`.
|
||||
///
|
||||
/// Notes:
|
||||
/// 1. This wrapper should be the most outer i.e.: `AutoSend<CacheMe<Bot>>`
|
||||
/// will automatically send requests, while `CacheMe<AutoSend<Bot>>` - won't.
|
||||
/// 2. After first call to `poll` on a request you will unable to access payload
|
||||
/// nor could you use [`send_ref`](Request::send_ref).
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use teloxide_core::{
|
||||
/// requests::{Requester, RequesterExt},
|
||||
/// types::User,
|
||||
/// Bot,
|
||||
/// };
|
||||
///
|
||||
/// # async {
|
||||
/// let bot = Bot::new("TOKEN").auto_send();
|
||||
/// let myself: User = bot.get_me().await?; // No .send()!
|
||||
/// # Ok::<_, teloxide_core::RequestError>(()) };
|
||||
/// ```
|
||||
pub struct AutoSend<B> {
|
||||
bot: B,
|
||||
}
|
||||
|
||||
impl<B> AutoSend<B> {
|
||||
/// Creates new `AutoSend`.
|
||||
///
|
||||
/// Note: it's recommended to use [`RequesterExt::auto_send`] instead.
|
||||
///
|
||||
/// [`RequesterExt::auto_send`]: crate::requests::RequesterExt::auto_send
|
||||
pub fn new(inner: B) -> AutoSend<B> {
|
||||
Self { bot: inner }
|
||||
}
|
||||
|
||||
/// Allows to access the inner bot.
|
||||
pub fn inner(&self) -> &B {
|
||||
&self.bot
|
||||
}
|
||||
|
||||
/// Unwraps the inner bot.
|
||||
pub fn into_inner(self) -> B {
|
||||
self.bot
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Requester> Requester for AutoSend<B> {
|
||||
type GetMe = AutoRequest<B::GetMe>;
|
||||
|
||||
fn get_me(&self) -> Self::GetMe {
|
||||
AutoRequest::new(self.bot.get_me())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct AutoRequest<R: Request>(#[pin] Inner<R>);
|
||||
|
||||
impl<R: Request> AutoRequest<R> {
|
||||
pub fn new(inner: R) -> Self {
|
||||
Self(Inner::Request(inner))
|
||||
}
|
||||
}
|
||||
|
||||
/// Data of the `AutoRequest` used to not expose variants (I wish there were
|
||||
/// private enum variants).
|
||||
#[pin_project::pin_project(project = InnerProj, project_replace = InnerRepl)]
|
||||
enum Inner<R: Request> {
|
||||
/// An unsent modifiable request.
|
||||
Request(R),
|
||||
/// A sent request.
|
||||
Future(#[pin] R::Send),
|
||||
/// Done state. Set after `R::Send::poll` returned `Ready(_)`.
|
||||
///
|
||||
/// Also used as a temporary replacement to turn pinned `Request(req)`
|
||||
/// into `Future(req.send())` in `AutoRequest::poll`.
|
||||
Done,
|
||||
}
|
||||
|
||||
impl<R: Request> Request for AutoRequest<R> {
|
||||
type Err = R::Err;
|
||||
type Send = R::Send;
|
||||
type SendRef = R::SendRef;
|
||||
|
||||
fn send(self) -> Self::Send {
|
||||
match self.0 {
|
||||
Inner::Request(req) => req.send(),
|
||||
Inner::Future(fut) => fut,
|
||||
Inner::Done => done_unreachable(),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_ref(&self) -> Self::SendRef {
|
||||
match &self.0 {
|
||||
Inner::Request(req) => req.send_ref(),
|
||||
Inner::Future(_) => already_polled(),
|
||||
Inner::Done => done_unreachable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Request> HasPayload for AutoRequest<R> {
|
||||
type Payload = R::Payload;
|
||||
|
||||
fn payload_mut(&mut self) -> &mut Self::Payload {
|
||||
match &mut self.0 {
|
||||
Inner::Request(req) => req.payload_mut(),
|
||||
Inner::Future(_) => already_polled(),
|
||||
Inner::Done => done_unreachable(),
|
||||
}
|
||||
}
|
||||
|
||||
fn payload_ref(&self) -> &Self::Payload {
|
||||
match &self.0 {
|
||||
Inner::Request(req) => req.payload_ref(),
|
||||
Inner::Future(_) => already_polled(),
|
||||
Inner::Done => done_unreachable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Request> Future for AutoRequest<R> {
|
||||
type Output = Result<Output<R>, R::Err>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this: Pin<&mut Inner<_>> = self.as_mut().project().0;
|
||||
|
||||
match this.as_mut().project() {
|
||||
// Poll the underling future.
|
||||
InnerProj::Future(fut) => {
|
||||
let res = futures::ready!(fut.poll(cx));
|
||||
// We've got the result, so we set the state to done.
|
||||
this.set(Inner::Done);
|
||||
Poll::Ready(res)
|
||||
}
|
||||
|
||||
// This future is fused.
|
||||
InnerProj::Done => Poll::Pending,
|
||||
// The `AutoRequest` future was polled for the first time after
|
||||
// creation. We need to transform it into sent form by calling
|
||||
// `R::send` and doing some magic around Pin.
|
||||
InnerProj::Request(_) => {
|
||||
// Replace `Request(_)` by `Done(_)` to obtain ownership over
|
||||
// the former.
|
||||
let inner = this.as_mut().project_replace(Inner::Done);
|
||||
// Map Request(req) to `Future(req.send())`.
|
||||
let inner = match inner {
|
||||
InnerRepl::Request(req) => Inner::Future(req.send()),
|
||||
// Practically this is unreachable, because we've just checked for
|
||||
// both `Future(_)` and `Done` variants.
|
||||
InnerRepl::Future(_) | InnerRepl::Done => done_unreachable(),
|
||||
};
|
||||
// Set the resulting `Future(_)` back to pin.
|
||||
this.set(inner);
|
||||
|
||||
// Poll `self`. This time another brunch will be executed, returning `Poll`.
|
||||
self.poll(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Request> FusedFuture for AutoRequest<R> {
|
||||
fn is_terminated(&self) -> bool {
|
||||
matches!(&self.0, Inner::Done)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn done_unreachable() -> ! {
|
||||
unreachable!("future is completed and as such doesn't provide any functionality")
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn already_polled() -> ! {
|
||||
panic!("AutoRequest was already polled once")
|
||||
}
|
|
@ -27,6 +27,8 @@ impl<B> CacheMe<B> {
|
|||
/// Creates new cache.
|
||||
///
|
||||
/// Note: it's recommended to use [`RequesterExt::cache_me`] instead.
|
||||
///
|
||||
/// [`RequesterExt::cache_me`]: crate::requests::RequesterExt::cache_me
|
||||
pub fn new(bot: B) -> CacheMe<B> {
|
||||
Self { bot, me: Arc::new(OnceCell::new()) }
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@ use crate::{
|
|||
};
|
||||
|
||||
mod api;
|
||||
mod auto_send;
|
||||
mod cache_me;
|
||||
mod download;
|
||||
|
||||
pub use auto_send::AutoSend;
|
||||
pub use cache_me::CacheMe;
|
||||
|
||||
pub(crate) const TELOXIDE_TOKEN: &str = "TELOXIDE_TOKEN";
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
//#![deny(missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
mod local_macros; // internal helper macros
|
||||
// The internal helper macros.
|
||||
mod local_macros;
|
||||
|
||||
// FIXME(waffle): rethink modules, find a place for wrappers.
|
||||
pub use self::{
|
||||
bot::{Bot, BotBuilder},
|
||||
bot::{AutoSend, Bot, BotBuilder, CacheMe},
|
||||
errors::{ApiErrorKind, DownloadError, KnownApiErrorKind, RequestError},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{bot::CacheMe, requests::Requester};
|
||||
use crate::{requests::Requester, AutoSend, CacheMe};
|
||||
|
||||
pub trait RequesterExt: Requester {
|
||||
/// Add `get_me` caching ability, see [`CacheMe`] for more.
|
||||
|
@ -8,6 +8,14 @@ pub trait RequesterExt: Requester {
|
|||
{
|
||||
CacheMe::new(self)
|
||||
}
|
||||
|
||||
/// Send requests automatically, see [`AutoSend`] for more.
|
||||
fn auto_send(self) -> AutoSend<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
AutoSend::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RequesterExt for T
|
||||
|
|
Loading…
Reference in a new issue