implement get_me caching

This commit is contained in:
Waffle 2020-09-22 22:54:52 +03:00
parent 2cabc5fb4e
commit 954c410c1b
6 changed files with 180 additions and 1 deletions

View file

@ -32,6 +32,7 @@ uuid = { version = "0.8.1", features = ["v4"] } # for attaching input files
derive_more = "0.99.9"
mime = "0.3.16"
thiserror = "1.0.20"
once_cell = "1.4.0"
[features]
# features those require nightly compiler

155
src/bot/cache_me.rs Normal file
View file

@ -0,0 +1,155 @@
use std::{pin::Pin, sync::Arc};
use futures::{
future,
future::{ok, Ready},
task::{Context, Poll},
Future,
};
use once_cell::sync::OnceCell;
use crate::{
payloads::GetMe,
requests::{HasPayload, Request, Requester},
types::User,
};
/// `get_me` cache.
///
/// Bot's user is hardly ever changed, so sometimes it's reasonable to cache
/// response from `get_me` method.
pub struct CacheMe<B> {
bot: B,
me: Arc<OnceCell<User>>,
}
impl<B> CacheMe<B> {
/// Creates new cache.
///
/// Note: it's recommended to use [`RequesterExt::cache_me`] instead.
pub fn new(bot: B) -> CacheMe<B> {
Self { bot, me: Arc::new(OnceCell::new()) }
}
/// Allows to access inner bot
pub fn inner(&self) -> &B {
&self.bot
}
/// Unwraps inner bot
pub fn into_inner(self) -> B {
self.bot
}
/// Clear cache.
///
/// Returns cached response from `get_me`, if it was cached.
///
/// Note: internally this uses [`Arc::make_mut`] so this will **not**
/// clear cache of clones of self.
pub fn clear(&mut self) -> Option<User> {
Arc::make_mut(&mut self.me).take()
}
}
impl<B: Requester> Requester for CacheMe<B> {
type GetMe = CachedMeRequest<B::GetMe>;
fn get_me(&self) -> Self::GetMe {
match self.me.get() {
Some(user) => CachedMeRequest(Inner::Ready(user.clone()), GetMe::new()),
None => CachedMeRequest(
Inner::Pending(self.bot.get_me(), Arc::clone(&self.me)),
GetMe::new(),
),
}
}
}
pub struct CachedMeRequest<R: Request<Payload = GetMe>>(Inner<R>, GetMe);
enum Inner<R: Request<Payload = GetMe>> {
Ready(User),
Pending(R, Arc<OnceCell<User>>),
}
impl<R: Request<Payload = GetMe>> Request for CachedMeRequest<R> {
type Err = R::Err;
type Send = Send<R>;
type SendRef = SendRef<R>;
fn send(self) -> Self::Send {
let fut = match self.0 {
Inner::Ready(user) => future::Either::Left(ok(user)),
Inner::Pending(req, cell) => future::Either::Right(Init(req.send(), cell)),
};
Send(fut)
}
fn send_ref(&self) -> Self::SendRef {
let fut = match &self.0 {
Inner::Ready(user) => future::Either::Left(ok(user.clone())),
Inner::Pending(req, cell) => {
future::Either::Right(Init(req.send_ref(), Arc::clone(cell)))
}
};
SendRef(fut)
}
}
impl<R: Request<Payload = GetMe>> HasPayload for CachedMeRequest<R> {
type Payload = GetMe;
fn payload_mut(&mut self) -> &mut Self::Payload {
&mut self.1
}
fn payload_ref(&self) -> &Self::Payload {
&self.1
}
}
type ReadyUser<Err> = Ready<Result<User, Err>>;
#[pin_project::pin_project]
pub struct Send<R: Request<Payload = GetMe>>(
#[pin] future::Either<ReadyUser<R::Err>, Init<R::Send, User>>,
);
impl<R: Request<Payload = GetMe>> Future for Send<R> {
type Output = Result<User, R::Err>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
this.0.poll(cx)
}
}
#[pin_project::pin_project]
pub struct SendRef<R: Request<Payload = GetMe>>(
#[pin] future::Either<ReadyUser<R::Err>, Init<R::SendRef, User>>,
);
impl<R: Request<Payload = GetMe>> Future for SendRef<R> {
type Output = Result<User, R::Err>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
this.0.poll(cx)
}
}
#[pin_project::pin_project]
struct Init<F, T>(#[pin] F, Arc<OnceCell<T>>);
impl<F: Future<Output = Result<T, E>>, T: Clone, E> Future for Init<F, T> {
type Output = Result<T, E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this.0.poll(cx) {
Poll::Ready(Ok(ok)) => Poll::Ready(Ok(this.1.get_or_init(|| ok).clone())),
poll @ Poll::Ready(_) | poll @ Poll::Pending => poll,
}
}
}

View file

@ -14,8 +14,11 @@ use crate::{
};
mod api;
mod cache_me;
mod download;
pub use cache_me::CacheMe;
pub(crate) const TELOXIDE_TOKEN: &str = "TELOXIDE_TOKEN";
pub(crate) const TELOXIDE_PROXY: &str = "TELOXIDE_PROXY";

View file

@ -1 +1 @@
pub use crate::payloads::{GetMeSetters as _};
pub use crate::payloads::GetMeSetters as _;

View file

@ -10,12 +10,14 @@ mod all;
mod json;
mod multipart;
mod requester;
mod requester_ext;
mod utils;
pub use all::*;
pub use json::JsonRequest;
pub use multipart::MultipartRequest;
pub use requester::Requester;
pub use requester_ext::RequesterExt;
/// A type that is returned after making a request to Telegram.
pub type ResponseResult<T> = Result<T, crate::RequestError>;

View file

@ -0,0 +1,18 @@
use crate::{bot::CacheMe, requests::Requester};
pub trait RequesterExt: Requester {
/// Add `get_me` caching ability, see [`CacheMe`] for more.
fn cache_me(self) -> CacheMe<Self>
where
Self: Sized,
{
CacheMe::new(self)
}
}
impl<T> RequesterExt for T
where
T: Requester,
{
/* use default impls */
}