Merge branch 'master' into fix_set_webhook_again

This commit is contained in:
Hirrolot 2022-09-25 07:19:01 +06:00 committed by GitHub
commit 3d1cb9c665
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 267 additions and 271 deletions

View file

@ -17,17 +17,12 @@ env:
CARGO_NET_RETRY: 10 CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10 RUSTUP_MAX_RETRIES: 10
rust_nightly: nightly-2022-01-17 rust_nightly: nightly-2022-09-23
# When updating this, also update: # When updating this, also update:
# - README.md # - README.md
# - src/lib.rs # - src/lib.rs
# - down below in a matrix # - down below in a matrix
rust_msrv: 1.58.0 rust_msrv: 1.64.0
# When updating this, also update:
# - down below in a matrix
#
# This is needed because some of our tests can't run on MSRV.
rust_msrv_dev: 1.59.0
CI: 1 CI: 1
@ -43,7 +38,6 @@ jobs:
- check-examples - check-examples
- clippy - clippy
- doc - doc
- msrv
steps: steps:
- run: exit 0 - run: exit 0
@ -82,7 +76,7 @@ jobs:
- stable - stable
- beta - beta
- nightly - nightly
- msrv_dev - msrv
include: include:
- rust: stable - rust: stable
@ -92,10 +86,10 @@ jobs:
toolchain: beta toolchain: beta
features: "--features full" features: "--features full"
- rust: nightly - rust: nightly
toolchain: nightly-2022-01-17 toolchain: nightly-2022-09-23
features: "--all-features" features: "--all-features"
- rust: msrv_dev - rust: msrv
toolchain: 1.59.0 toolchain: 1.64.0
features: "--features full" features: "--features full"
steps: steps:
@ -108,6 +102,7 @@ jobs:
profile: minimal profile: minimal
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
override: true override: true
components: rustfmt # required by codegen
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@v1 uses: Swatinem/rust-cache@v1
@ -194,27 +189,3 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: docs # from .cargo/config.toml command: docs # from .cargo/config.toml
msrv:
name: minimal supported rust version
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Rust ${{ env.rust_msrv }}
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_msrv }}
override: true
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1
- name: Check
uses: actions-rs/cargo@v1
with:
command: check
args: --verbose --features full

View file

@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## unreleased ## unreleased
### Changed
- **You can now `.await` any `Request`!** ([#249][pr249])
- `Request` now requires `Self: IntoFuture`
- There is no need for `AutoSend` anymore
- MSRV (Minimal Supported Rust Version) was bumped from `1.58.0` to `1.64.0`
### Removed ### Removed
- Methods for creating `InlineQuery` ([#246][pr244]) - Methods for creating `InlineQuery` ([#246][pr244])
@ -19,6 +26,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[pr250]: https://github.com/teloxide/teloxide-core/pull/250 [pr250]: https://github.com/teloxide/teloxide-core/pull/250
### Deprecated
- `AutoSend` adaptor ([#249][pr249])
[pr249]: https://github.com/teloxide/teloxide-core/pull/249
## 0.7.1 - 2022-08-19 ## 0.7.1 - 2022-08-19
### Fixed ### Fixed

View file

@ -24,7 +24,7 @@ exclude = [
futures = "0.3.5" futures = "0.3.5"
tokio = { version = "1.12.0", features = ["fs"] } tokio = { version = "1.12.0", features = ["fs"] }
tokio-util = { version = "0.7.0", features = ["codec"] } tokio-util = { version = "0.7.0", features = ["codec"] }
pin-project = "1.0.3" pin-project = "1.0.12"
bytes = "1.0.0" bytes = "1.0.0"
reqwest = { version = "0.11.10", features = ["json", "stream", "multipart"], default-features = false } reqwest = { version = "0.11.10", features = ["json", "stream", "multipart"], default-features = false }
url = { version = "2", features = ["serde"] } url = { version = "2", features = ["serde"] }
@ -99,8 +99,8 @@ cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples=examples"]
[[example]] [[example]]
name = "self_info" name = "self_info"
required-features = ["tokio/macros", "tokio/rt-multi-thread", "auto_send"] required-features = ["tokio/macros", "tokio/rt-multi-thread"]
[[example]] [[example]]
name = "erased" name = "erased"
required-features = ["tokio/macros", "tokio/rt-multi-thread", "auto_send", "erased"] required-features = ["tokio/macros", "tokio/rt-multi-thread", "erased", "trace_adaptor"]

View file

@ -27,7 +27,7 @@
```toml ```toml
teloxide-core = "0.7" teloxide-core = "0.7"
``` ```
_Compiler support: requires rustc 1.58+_. _Compiler support: requires rustc 1.64+_.
[`teloxide`]: https://docs.rs/teloxide [`teloxide`]: https://docs.rs/teloxide
[Telegram Bot API]: https://core.telegram.org/bots/api [Telegram Bot API]: https://core.telegram.org/bots/api

View file

@ -32,9 +32,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
log::info!("Trace settings: {:?}", trace_settings); log::info!("Trace settings: {:?}", trace_settings);
let bot = if trace_settings.is_empty() { let bot = if trace_settings.is_empty() {
Bot::from_env().erase().auto_send() Bot::from_env().erase()
} else { } else {
Bot::from_env().trace(trace_settings).erase().auto_send() Bot::from_env().trace(trace_settings).erase()
}; };
bot.send_chat_action(chat_id, ChatAction::Typing).await?; bot.send_chat_action(chat_id, ChatAction::Typing).await?;

View file

@ -13,9 +13,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.parse::<i64>()?, .parse::<i64>()?,
); );
let bot = Bot::from_env() let bot = Bot::from_env().parse_mode(ParseMode::MarkdownV2);
.parse_mode(ParseMode::MarkdownV2)
.auto_send();
let Me { user: me, .. } = bot.get_me().await?; let Me { user: me, .. } = bot.get_me().await?;

View file

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "nightly-2022-01-17" channel = "nightly-2022-09-23"
components = ["rustfmt", "clippy"] components = ["rustfmt", "clippy"]
profile = "minimal" profile = "minimal"

View file

@ -3,17 +3,18 @@
//! Bot adaptors are very similar to the [`Iterator`] adaptors: they are bots //! Bot adaptors are very similar to the [`Iterator`] adaptors: they are bots
//! wrapping other bots to alter existing or add new functionality. //! wrapping other bots to alter existing or add new functionality.
//! //!
//! E.g. [`AutoSend`] allows `await`ing requests directly, no need to use
//! `.send()`.
//!
//! [`Requester`]: crate::requests::Requester //! [`Requester`]: crate::requests::Requester
/// [`AutoSend`] bot adaptor which allows sending a request without calling /// [`AutoSend`] bot adaptor which used to allow sending a request without
/// [`send`]. /// calling [`send`].
/// ///
/// [`AutoSend`]: auto_send::AutoSend /// [`AutoSend`]: auto_send::AutoSend
/// [`send`]: crate::requests::Request::send /// [`send`]: crate::requests::Request::send
#[cfg(feature = "auto_send")] #[cfg(feature = "auto_send")]
#[deprecated(
since = "0.8.0",
note = "`AutoSend` is no longer required to `.await` requests and is now noop"
)]
pub mod auto_send; pub mod auto_send;
/// [`CacheMe`] bot adaptor which caches [`GetMe`] requests. /// [`CacheMe`] bot adaptor which caches [`GetMe`] requests.
@ -47,6 +48,7 @@ pub mod throttle;
mod parse_mode; mod parse_mode;
#[cfg(feature = "auto_send")] #[cfg(feature = "auto_send")]
#[allow(deprecated)]
pub use auto_send::AutoSend; pub use auto_send::AutoSend;
#[cfg(feature = "cache_me")] #[cfg(feature = "cache_me")]
pub use cache_me::CacheMe; pub use cache_me::CacheMe;

View file

@ -1,10 +1,5 @@
use std::{ use std::future::IntoFuture;
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures::future::FusedFuture;
use url::Url; use url::Url;
use crate::{ use crate::{
@ -12,32 +7,16 @@ use crate::{
types::*, types::*,
}; };
/// Send requests automatically. /// Previously was used to send requests automatically.
/// ///
/// Requests returned by `<AutoSend<_> as `[`Requester`]`>` are [`Future`]s /// Before addition of [`IntoFuture`] you could only `.await` [`Future`]s.
/// which means that you can simply `.await` them instead of using /// This adaptor turned requests into futures, allowing to `.await` them,
/// `.send().await`. /// without calling `.send()`.
/// ///
/// Notes: /// Now, however, all requests are required to implement `IntoFuture`, allowing
/// 1. This wrapper should be the most outer i.e.: `AutoSend<CacheMe<Bot>>` /// you to `.await` them directly. This adaptor is noop, and shouldn't be used.
/// will automatically send requests, while `CacheMe<AutoSend<Bot>>` - won't.
/// 2. After first call to `poll` on a request you will be unable to access
/// payload nor could you use [`send_ref`](Request::send_ref).
/// ///
/// ## Examples /// [`Future`]: std::future::Future
///
/// ```rust
/// use teloxide_core::{
/// requests::{Requester, RequesterExt},
/// types::Me,
/// Bot,
/// };
///
/// # async {
/// let bot = Bot::new("TOKEN").auto_send();
/// let myself: Me = bot.get_me().await?; // No .send()!
/// # Ok::<_, teloxide_core::RequestError>(()) };
/// ```
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AutoSend<B> { pub struct AutoSend<B> {
bot: B, bot: B,
@ -192,33 +171,17 @@ download_forward! {
} }
#[must_use = "Futures are lazy and do nothing unless polled or awaited"] #[must_use = "Futures are lazy and do nothing unless polled or awaited"]
#[pin_project::pin_project] pub struct AutoRequest<R>(R);
pub struct AutoRequest<R: Request>(#[pin] Inner<R>);
impl<R> AutoRequest<R> impl<R> AutoRequest<R>
where where
R: Request, R: Request,
{ {
pub fn new(inner: R) -> Self { pub fn new(inner: R) -> Self {
Self(Inner::Request(inner)) Self(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 for AutoRequest<R> impl<R> Request for AutoRequest<R>
where where
R: Request, R: Request,
@ -228,95 +191,31 @@ where
type SendRef = R::SendRef; type SendRef = R::SendRef;
fn send(self) -> Self::Send { fn send(self) -> Self::Send {
match self.0 { self.0.send()
Inner::Request(req) => req.send(),
Inner::Future(fut) => fut,
Inner::Done => done_unreachable(),
}
} }
fn send_ref(&self) -> Self::SendRef { fn send_ref(&self) -> Self::SendRef {
match &self.0 { self.0.send_ref()
Inner::Request(req) => req.send_ref(),
Inner::Future(_) => already_polled(),
Inner::Done => done_unreachable(),
} }
} }
impl<R: Request> IntoFuture for AutoRequest<R> {
type Output = Result<Output<Self>, <Self as Request>::Err>;
type IntoFuture = <Self as Request>::Send;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
} }
impl<R: Request> HasPayload for AutoRequest<R> { impl<R: Request> HasPayload for AutoRequest<R> {
type Payload = R::Payload; type Payload = R::Payload;
fn payload_mut(&mut self) -> &mut Self::Payload { fn payload_mut(&mut self) -> &mut Self::Payload {
match &mut self.0 { self.0.payload_mut()
Inner::Request(req) => req.payload_mut(),
Inner::Future(_) => already_polled(),
Inner::Done => done_unreachable(),
}
} }
fn payload_ref(&self) -> &Self::Payload { fn payload_ref(&self) -> &Self::Payload {
match &self.0 { self.0.payload_ref()
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")
}

View file

@ -1,4 +1,4 @@
use std::{pin::Pin, sync::Arc}; use std::{future::IntoFuture, pin::Pin, sync::Arc};
use futures::{ use futures::{
future, future,
@ -244,6 +244,15 @@ impl<R: Request<Payload = GetMe>> HasPayload for CachedMeRequest<R> {
} }
} }
impl<R: Request<Payload = GetMe>> IntoFuture for CachedMeRequest<R> {
type Output = Result<Me, R::Err>;
type IntoFuture = Send<R>;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
}
type ReadyMe<Err> = Ready<Result<Me, Err>>; type ReadyMe<Err> = Ready<Result<Me, Err>>;
#[pin_project::pin_project] #[pin_project::pin_project]

View file

@ -1,4 +1,4 @@
use std::sync::Arc; use std::{future::IntoFuture, sync::Arc};
use futures::{future::BoxFuture, FutureExt}; use futures::{future::BoxFuture, FutureExt};
use reqwest::Url; use reqwest::Url;
@ -51,7 +51,8 @@ pub struct ErasedRequest<'a, T, E> {
inner: Box<dyn ErasableRequest<'a, Payload = T, Err = E> + 'a>, inner: Box<dyn ErasableRequest<'a, Payload = T, Err = E> + 'a>,
} }
impl<'a, T, E> ErasedRequest<'a, T, E> { // `T: Payload` required b/c of <https://github.com/rust-lang/rust/issues/102185>
impl<'a, T: Payload, E> ErasedRequest<'a, T, E> {
pub(crate) fn erase(request: impl Request<Payload = T, Err = E> + 'a) -> Self { pub(crate) fn erase(request: impl Request<Payload = T, Err = E> + 'a) -> Self {
Self { Self {
inner: Box::new(request), inner: Box::new(request),
@ -94,6 +95,19 @@ where
} }
} }
impl<'a, T, E> IntoFuture for ErasedRequest<'a, T, E>
where
T: Payload,
E: std::error::Error + Send,
{
type Output = Result<Output<Self>, <Self as Request>::Err>;
type IntoFuture = <Self as Request>::Send;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
}
/// Object safe version of [`Request`]. /// Object safe version of [`Request`].
/// ///
/// TODO(waffle): make [`Request`] object safe and remove this trait (this is a /// TODO(waffle): make [`Request`] object safe and remove this trait (this is a

View file

@ -1,4 +1,9 @@
use std::{future::Future, pin::Pin, sync::Arc, time::Instant}; use std::{
future::{Future, IntoFuture},
pin::Pin,
sync::Arc,
time::Instant,
};
use futures::{ use futures::{
future::BoxFuture, future::BoxFuture,
@ -74,6 +79,20 @@ where
} }
} }
impl<R> IntoFuture for ThrottlingRequest<R>
where
R: Request + Clone + Send + Sync + 'static,
R::Err: AsResponseParameters + Send,
Output<R>: Send,
{
type Output = Result<Output<Self>, <Self as Request>::Err>;
type IntoFuture = <Self as Request>::Send;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
}
impl<R: Request> Future for ThrottlingSend<R> impl<R: Request> Future for ThrottlingSend<R>
where where
R::Err: AsResponseParameters, R::Err: AsResponseParameters,
@ -159,7 +178,7 @@ where
let res = match &mut request { let res = match &mut request {
ShareableRequest::Shared(shared) => shared.send_ref().await, ShareableRequest::Shared(shared) => shared.send_ref().await,
ShareableRequest::Owned(owned) => owned.take().unwrap().send().await, ShareableRequest::Owned(owned) => owned.take().unwrap().await,
}; };
return res; return res;
@ -178,7 +197,7 @@ where
request.send_ref().await request.send_ref().await
} }
(false, ShareableRequest::Shared(shared)) => shared.send_ref().await, (false, ShareableRequest::Shared(shared)) => shared.send_ref().await,
(false, ShareableRequest::Owned(owned)) => owned.take().unwrap().send().await, (false, ShareableRequest::Owned(owned)) => owned.take().unwrap().await,
}; };
let retry_after = res.as_ref().err().and_then(<_>::retry_after); let retry_after = res.as_ref().err().and_then(<_>::retry_after);

View file

@ -9,7 +9,7 @@ use vecrem::VecExt;
use crate::{ use crate::{
adaptors::throttle::{request_lock::RequestLock, ChatIdHash, Limits, Settings}, adaptors::throttle::{request_lock::RequestLock, ChatIdHash, Limits, Settings},
errors::AsResponseParameters, errors::AsResponseParameters,
requests::{Request, Requester}, requests::Requester,
}; };
const MINUTE: Duration = Duration::from_secs(60); const MINUTE: Duration = Duration::from_secs(60);
@ -321,7 +321,7 @@ async fn freeze(
// TODO: maybe not call `get_chat` every time? // TODO: maybe not call `get_chat` every time?
// At this point there isn't much we can do with the error besides ignoring // At this point there isn't much we can do with the error besides ignoring
if let Ok(chat) = bot.get_chat(id).send().await { if let Ok(chat) = bot.get_chat(id).await {
match chat.slow_mode_delay() { match chat.slow_mode_delay() {
Some(delay) => { Some(delay) => {
let now = Instant::now(); let now = Instant::now();

View file

@ -1,6 +1,6 @@
use std::{ use std::{
fmt::Debug, fmt::Debug,
future::Future, future::{Future, IntoFuture},
pin::Pin, pin::Pin,
task::{self, Poll}, task::{self, Poll},
}; };
@ -309,6 +309,21 @@ where
} }
} }
impl<R> IntoFuture for TraceRequest<R>
where
R: Request,
Output<R>: Debug,
R::Err: Debug,
R::Payload: Debug,
{
type Output = Result<Output<Self>, <Self as Request>::Err>;
type IntoFuture = <Self as Request>::Send;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
}
#[pin_project::pin_project] #[pin_project::pin_project]
pub struct Send<F> pub struct Send<F>
where where

View file

@ -29,7 +29,7 @@ const TELOXIDE_TOKEN: &str = "TELOXIDE_TOKEN";
/// use teloxide_core::prelude::*; /// use teloxide_core::prelude::*;
/// ///
/// let bot = Bot::new("TOKEN"); /// let bot = Bot::new("TOKEN");
/// dbg!(bot.get_me().send().await?); /// dbg!(bot.get_me().await?);
/// # Ok::<_, teloxide_core::RequestError>(()) }; /// # Ok::<_, teloxide_core::RequestError>(()) };
/// ``` /// ```
/// ///
@ -158,7 +158,7 @@ impl Bot {
/// let url = reqwest::Url::parse("https://localhost/tbas").unwrap(); /// let url = reqwest::Url::parse("https://localhost/tbas").unwrap();
/// let bot = Bot::new("TOKEN").set_api_url(url); /// let bot = Bot::new("TOKEN").set_api_url(url);
/// // From now all methods will use "https://localhost/tbas" as an API URL. /// // From now all methods will use "https://localhost/tbas" as an API URL.
/// bot.get_me().send().await /// bot.get_me().await
/// # }; /// # };
/// ``` /// ```
/// ///

View file

@ -23,7 +23,7 @@ use xshell::{cmd, Shell};
fn ensure_rustfmt(sh: &Shell) { fn ensure_rustfmt(sh: &Shell) {
// FIXME(waffle): find a better way to set toolchain // FIXME(waffle): find a better way to set toolchain
let toolchain = "nightly-2022-01-17"; let toolchain = "nightly-2022-09-23";
let version = cmd!(sh, "rustup run {toolchain} rustfmt --version") let version = cmd!(sh, "rustup run {toolchain} rustfmt --version")
.read() .read()
@ -38,7 +38,7 @@ fn ensure_rustfmt(sh: &Shell) {
} }
pub fn reformat(text: String) -> String { pub fn reformat(text: String) -> String {
let toolchain = "nightly-2022-01-17"; let toolchain = "nightly-2022-09-23";
let sh = Shell::new().unwrap(); let sh = Shell::new().unwrap();
ensure_rustfmt(&sh); ensure_rustfmt(&sh);

View file

@ -7,10 +7,9 @@
//!```toml //!```toml
//! teloxide_core = "0.7" //! teloxide_core = "0.7"
//! ``` //! ```
//! _Compiler support: requires rustc 1.58+_. //! _Compiler support: requires rustc 1.64+_.
//! //!
//! ``` //! ```
//! # #[cfg(feature = "auto_send")]
//! # async { //! # async {
//! # let chat_id = teloxide_core::types::ChatId(-1); //! # let chat_id = teloxide_core::types::ChatId(-1);
//! use teloxide_core::{ //! use teloxide_core::{
@ -18,9 +17,7 @@
//! types::{DiceEmoji, ParseMode}, //! types::{DiceEmoji, ParseMode},
//! }; //! };
//! //!
//! let bot = Bot::from_env() //! let bot = Bot::from_env().parse_mode(ParseMode::MarkdownV2);
//! .parse_mode(ParseMode::MarkdownV2)
//! .auto_send();
//! //!
//! let me = bot.get_me().await?; //! let me = bot.get_me().await?;
//! //!
@ -46,7 +43,6 @@
//! - `native-tls` = use [`native-tls`] tls implementation (**enabled by //! - `native-tls` = use [`native-tls`] tls implementation (**enabled by
//! default**) //! default**)
//! - `rustls` — use [`rustls`] tls implementation //! - `rustls` — use [`rustls`] tls implementation
//! - `auto_send` — enables [`AutoSend`] bot adaptor
//! - `trace_adaptor` — enables [`Trace`] bot adaptor //! - `trace_adaptor` — enables [`Trace`] bot adaptor
//! - `erased` — enables [`ErasedRequester`] bot adaptor //! - `erased` — enables [`ErasedRequester`] bot adaptor
//! - `throttle` — enables [`Throttle`] bot adaptor //! - `throttle` — enables [`Throttle`] bot adaptor
@ -55,6 +51,7 @@
//! - `nightly` — enables nightly-only features, currently: //! - `nightly` — enables nightly-only features, currently:
//! - Removes some future boxing using `#![feature(type_alias_impl_trait)]` //! - Removes some future boxing using `#![feature(type_alias_impl_trait)]`
//! - Used to built docs (`#![feature(doc_cfg, doc_notable_trait)]`) //! - Used to built docs (`#![feature(doc_cfg, doc_notable_trait)]`)
//! - `auto_send` — enables [`AutoSend`] bot adaptor (deprecated)
//! //!
//! [`AutoSend`]: adaptors::AutoSend //! [`AutoSend`]: adaptors::AutoSend
//! [`Trace`]: adaptors::Trace //! [`Trace`]: adaptors::Trace
@ -93,6 +90,13 @@
#![allow(clippy::return_self_not_must_use)] #![allow(clippy::return_self_not_must_use)]
// Workaround for CI // Workaround for CI
#![allow(rustdoc::bare_urls)] #![allow(rustdoc::bare_urls)]
// FIXME: deal with these lints
#![allow(
clippy::collapsible_str_replace,
clippy::borrow_deref_ref,
clippy::unnecessary_lazy_evaluations,
clippy::derive_partial_eq_without_eq
)]
// The internal helper macros. // The internal helper macros.
#[macro_use] #[macro_use]

View file

@ -42,7 +42,7 @@ pub trait Download<'w>
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> { /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// let bot = Bot::new("TOKEN"); /// let bot = Bot::new("TOKEN");
/// ///
/// let TgFile { file_path, .. } = bot.get_file("*file_id*").send().await?; /// let TgFile { file_path, .. } = bot.get_file("*file_id*").await?;
/// let mut file = File::create("/tmp/test.png").await?; /// let mut file = File::create("/tmp/test.png").await?;
/// bot.download_file(&file_path, &mut file).await?; /// bot.download_file(&file_path, &mut file).await?;
/// # Ok(()) } /// # Ok(()) }

View file

@ -1,3 +1,5 @@
use std::future::IntoFuture;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use crate::{ use crate::{
@ -49,6 +51,20 @@ where
} }
} }
impl<P> IntoFuture for JsonRequest<P>
where
P: 'static,
P: Payload + Serialize,
P::Output: DeserializeOwned,
{
type Output = Result<P::Output, RequestError>;
type IntoFuture = <Self as Request>::Send;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
}
impl<P> HasPayload for JsonRequest<P> impl<P> HasPayload for JsonRequest<P>
where where
P: Payload, P: Payload,

View file

@ -1,3 +1,5 @@
use std::future::IntoFuture;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use crate::{ use crate::{
@ -50,6 +52,20 @@ where
} }
} }
impl<P> IntoFuture for MultipartRequest<P>
where
P: 'static,
P: Payload + MultipartPayload + Serialize,
P::Output: DeserializeOwned,
{
type Output = Result<P::Output, RequestError>;
type IntoFuture = <Self as Request>::Send;
fn into_future(self) -> Self::IntoFuture {
self.send()
}
}
impl<P> HasPayload for MultipartRequest<P> impl<P> HasPayload for MultipartRequest<P>
where where
P: Payload, P: Payload,

View file

@ -1,7 +1,7 @@
use std::future::Future; use std::future::{Future, IntoFuture};
use either::Either; // use either::Either;
use futures::future; // use futures::future;
use crate::requests::{HasPayload, Output}; use crate::requests::{HasPayload, Output};
@ -20,12 +20,11 @@ use crate::requests::{HasPayload, Output};
/// ///
/// [`Throttle<B>`]: crate::adaptors::Throttle /// [`Throttle<B>`]: crate::adaptors::Throttle
#[cfg_attr(all(any(docsrs, dep_docsrs), feature = "nightly"), doc(notable_trait))] #[cfg_attr(all(any(docsrs, dep_docsrs), feature = "nightly"), doc(notable_trait))]
pub trait Request: HasPayload { pub trait Request
/* where
* Could be mostly `core::future::IntoFuture` though there is no reason to Self: HasPayload,
* use it before it's integrated in async/await Self: IntoFuture<Output = Result<Output<Self>, Self::Err>, IntoFuture = Self::Send>,
*/ {
/// The type of an error that may happen while sending a request to /// The type of an error that may happen while sending a request to
/// Telegram. /// Telegram.
type Err: std::error::Error + Send; type Err: std::error::Error + Send;
@ -42,6 +41,7 @@ pub trait Request: HasPayload {
/// Send this request. /// Send this request.
/// ///
/// ## Examples /// ## Examples
///
/// ``` /// ```
/// # async { /// # async {
/// use teloxide_core::{ /// use teloxide_core::{
@ -56,7 +56,11 @@ pub trait Request: HasPayload {
/// // Note: it's recommended to `Requester` instead of creating requests directly /// // Note: it's recommended to `Requester` instead of creating requests directly
/// let method = GetMe::new(); /// let method = GetMe::new();
/// let request = JsonRequest::new(bot, method); /// let request = JsonRequest::new(bot, method);
/// let request_clone = request.clone();
/// let _: Me = request.send().await.unwrap(); /// let _: Me = request.send().await.unwrap();
///
/// // You can also just await requests, without calling `send`:
/// let _: Me = request_clone.await.unwrap();
/// # }; /// # };
/// ``` /// ```
#[must_use = "Futures are lazy and do nothing unless polled or awaited"] #[must_use = "Futures are lazy and do nothing unless polled or awaited"]
@ -73,6 +77,7 @@ pub trait Request: HasPayload {
/// and then serializing it, this method should just serialize the data.) /// and then serializing it, this method should just serialize the data.)
/// ///
/// ## Examples /// ## Examples
///
/// ``` /// ```
/// # async { /// # async {
/// use teloxide_core::{prelude::*, requests::Request, types::ChatId, Bot}; /// use teloxide_core::{prelude::*, requests::Request, types::ChatId, Bot};
@ -99,27 +104,30 @@ pub trait Request: HasPayload {
} }
} }
impl<L, R> Request for Either<L, R> // FIXME: re-introduce `Either` impls once `Either: IntoFuture` (or make out own
where // `Either`) (same for `Requester`)
L: Request,
R: Request<Payload = L::Payload, Err = L::Err>,
{
type Err = L::Err;
type Send = future::Either<L::Send, R::Send>; // impl<L, R> Request for Either<L, R>
// where
// L: Request,
// R: Request<Payload = L::Payload, Err = L::Err>,
// {
// type Err = L::Err;
type SendRef = future::Either<L::SendRef, R::SendRef>; // type Send = future::Either<L::Send, R::Send>;
fn send(self) -> Self::Send { // type SendRef = future::Either<L::SendRef, R::SendRef>;
self.map_left(<_>::send)
.map_right(<_>::send)
.either(future::Either::Left, future::Either::Right)
}
fn send_ref(&self) -> Self::SendRef { // fn send(self) -> Self::Send {
self.as_ref() // self.map_left(<_>::send)
.map_left(<_>::send_ref) // .map_right(<_>::send)
.map_right(<_>::send_ref) // .either(future::Either::Left, future::Either::Right)
.either(future::Either::Left, future::Either::Right) // }
}
} // fn send_ref(&self) -> Self::SendRef {
// self.as_ref()
// .map_left(<_>::send_ref)
// .map_right(<_>::send_ref)
// .either(future::Either::Left, future::Either::Right)
// }
// }

View file

@ -32,8 +32,7 @@ use crate::{
/// bot.send_message(chat_id, "<b>Text</b>") /// bot.send_message(chat_id, "<b>Text</b>")
/// // Optional parameters can be supplied by calling setters /// // Optional parameters can be supplied by calling setters
/// .parse_mode(ParseMode::Html) /// .parse_mode(ParseMode::Html)
/// // To send request to telegram you need to call `.send()` and await the resulting future /// // To send request to telegram you need to `.await` the request
/// .send()
/// .await?; /// .await?;
/// # Ok::<_, teloxide_core::RequestError>(()) /// # Ok::<_, teloxide_core::RequestError>(())
/// # }; /// # };
@ -51,7 +50,7 @@ use crate::{
/// where /// where
/// R: Requester, /// R: Requester,
/// { /// {
/// bot.send_message(chat, "hi").send().await.expect("error") /// bot.send_message(chat, "hi").await.expect("error")
/// } /// }
/// ``` /// ```
#[cfg_attr(all(any(docsrs, dep_docsrs), feature = "nightly"), doc(notable_trait))] #[cfg_attr(all(any(docsrs, dep_docsrs), feature = "nightly"), doc(notable_trait))]
@ -1090,30 +1089,30 @@ where
forward_all! {} forward_all! {}
} }
macro_rules! fty_either { // macro_rules! fty_either {
($T:ident) => { // ($T:ident) => {
either::Either<LR::$T, RR::$T> // either::Either<LR::$T, RR::$T>
}; // };
} // }
macro_rules! fwd_either { // macro_rules! fwd_either {
($m:ident $this:ident ($($arg:ident : $T:ty),*)) => { // ($m:ident $this:ident ($($arg:ident : $T:ty),*)) => {
match ($this) { // match ($this) {
either::Either::Left(l) => either::Either::Left(l.$m($($arg),*)), // either::Either::Left(l) => either::Either::Left(l.$m($($arg),*)),
either::Either::Right(r) => either::Either::Right(r.$m($($arg),*)), // either::Either::Right(r) =>
} // either::Either::Right(r.$m($($arg),*)), }
}; // };
} // }
impl<LR, RR> Requester for either::Either<LR, RR> // impl<LR, RR> Requester for either::Either<LR, RR>
where // where
LR: Requester, // LR: Requester,
RR: Requester<Err = LR::Err>, // RR: Requester<Err = LR::Err>,
{ // {
type Err = LR::Err; // type Err = LR::Err;
forward_all! { fwd_either, fty_either } // forward_all! { fwd_either, fty_either }
} // }
#[test] #[test]
fn codegen_requester_methods() { fn codegen_requester_methods() {

View file

@ -4,6 +4,7 @@ use crate::{adaptors::DefaultParseMode, requests::Requester, types::ParseMode};
use crate::adaptors::CacheMe; use crate::adaptors::CacheMe;
#[cfg(feature = "auto_send")] #[cfg(feature = "auto_send")]
#[allow(deprecated)]
use crate::adaptors::AutoSend; use crate::adaptors::AutoSend;
#[cfg(feature = "erased")] #[cfg(feature = "erased")]
@ -28,6 +29,11 @@ pub trait RequesterExt: Requester {
/// Send requests automatically, see [`AutoSend`] for more. /// Send requests automatically, see [`AutoSend`] for more.
#[cfg(feature = "auto_send")] #[cfg(feature = "auto_send")]
#[deprecated(
since = "0.8.0",
note = "`AutoSend` is no longer required to `.await` requests and is now noop"
)]
#[allow(deprecated)]
fn auto_send(self) -> AutoSend<Self> fn auto_send(self) -> AutoSend<Self>
where where
Self: Sized, Self: Sized,

View file

@ -17,26 +17,8 @@ pub struct KeyboardButton {
/// Request something from user. This is available in private chats only. /// Request something from user. This is available in private chats only.
/// ///
/// - If `Some(Contact)`, the user's phone number will be sent as a contact /// See [`ButtonRequest`] documentation for options on what can be
/// when the button is pressed. /// requested.
/// - If `Some(Location)`, the user's current location will be sent when the
/// button is pressed.
/// - If `Some(Poll(_))`, the user will be asked to create a poll and send
/// it to the bot when the button is pressed.
/// - If `Some(WebApp(_))`, the described Web App will be launched when the
/// button is pressed. The Web App will be able to send a “web_app_data”
/// service message.
///
/// **Note:** `Contact` and `Location` options will only work in Telegram
/// versions released after 9 April, 2016. Older clients will display
/// unsupported message.
///
/// **Note:** `Poll(_)` option will only work in Telegram versions released
/// after 23 January, 2020. Older clients will display unsupported message.
///
/// **Note:** `WebApp(_)` option will only work in Telegram versions
/// released after 16 April, 2022. Older clients will display unsupported
/// message.
#[serde(flatten)] #[serde(flatten)]
pub request: Option<ButtonRequest>, pub request: Option<ButtonRequest>,
} }
@ -61,13 +43,38 @@ impl KeyboardButton {
} }
} }
// Serialize + Deserialize are implemented by hand /// Request something from user, when a button is pressed.
// FIXME: add documentation ///
#[derive(Clone, Debug, Eq, Hash, PartialEq)] /// See individual variants documentation for more info.
#[derive(Clone, Debug, Eq, Hash, PartialEq /*, Serialize, Deserialize */)]
pub enum ButtonRequest { pub enum ButtonRequest {
/// If this variant is used, the user's current location will be sent when
/// the button is pressed.
///
/// **Note:** this option will only work in Telegram versions released after
/// 9 April, 2016. Older clients will display unsupported message.
Location, Location,
/// If this variant is used, the user's phone number will be sent as a
/// contact when the button is pressed.
///
/// **Note:** this option will only work in Telegram versions released after
/// 9 April, 2016. Older clients will display unsupported message.
Contact, Contact,
/// If this variant is used, the user will be asked to create a poll and
/// send it to the bot when the button is pressed.
///
/// **Note:** this option will only work in Telegram versions released after
/// 23 January, 2020. Older clients will display unsupported message.
Poll(KeyboardButtonPollType), Poll(KeyboardButtonPollType),
/// If this variant is used, the described Web App will be launched when the
/// button is pressed. The Web App will be able to send a “web_app_data”
/// service message.
///
/// **Note:** this option will only work in Telegram versions released after
/// 16 April, 2022. Older clients will display unsupported message.
WebApp(WebAppInfo), WebApp(WebAppInfo),
} }