From e4f30e729a5c745eb48f08e98f9b22f10b18a71d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 28 Feb 2023 20:45:39 +0400 Subject: [PATCH 01/26] Simplify release process by using `cargo-release` --- Cargo.toml | 4 ++++ crates/teloxide-core/Cargo.toml | 8 ++++++++ crates/teloxide-core/src/lib.rs | 2 +- crates/teloxide-macros/Cargo.toml | 8 ++++++-- crates/teloxide/Cargo.toml | 7 +++++++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09124e33..d5041ade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,7 @@ edition = "2021" license = "MIT" homepage = "https://github.com/teloxide/teloxide" repository = "https://github.com/teloxide/teloxide" + +[workspace.metadata.release] +tag-message = "Release {{crate_name}} version {{version}}" +tag-name = "{{prefix}}v{{version}}" diff --git a/crates/teloxide-core/Cargo.toml b/crates/teloxide-core/Cargo.toml index 4df1fc7b..7cf15a1d 100644 --- a/crates/teloxide-core/Cargo.toml +++ b/crates/teloxide-core/Cargo.toml @@ -98,6 +98,14 @@ rustdoc-args = ["--cfg", "docsrs", "-Znormalize-docs"] # https://github.com/rust-lang/rust/issues/88791 cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] +[package.metadata.release] +tag-prefix = "core-" +enable-features = ["full"] +pre-release-replacements = [ + {file="README.md", search="teloxide-core = \"[^\"]+\"", replace="teloxide-core = \"{{version}}\""}, + {file="src/lib.rs", search="teloxide-core = \"[^\"]+\"", replace="teloxide-core = \"{{version}}\""}, + {file="CHANGELOG.md", search="## unreleased", replace="## unreleased\n\n## {{version}} - {{date}}", exactly=1}, +] [[example]] name = "self_info" diff --git a/crates/teloxide-core/src/lib.rs b/crates/teloxide-core/src/lib.rs index bfc94ddf..7cde5201 100644 --- a/crates/teloxide-core/src/lib.rs +++ b/crates/teloxide-core/src/lib.rs @@ -5,7 +5,7 @@ //! asynchronous and built using [`tokio`]. //! //!```toml -//! teloxide_core = "0.9" +//! teloxide-core = "0.9" //! ``` //! _Compiler support: requires rustc 1.64+_. //! diff --git a/crates/teloxide-macros/Cargo.toml b/crates/teloxide-macros/Cargo.toml index e81ffbb1..b536ce16 100644 --- a/crates/teloxide-macros/Cargo.toml +++ b/crates/teloxide-macros/Cargo.toml @@ -13,13 +13,17 @@ documentation = "https://docs.rs/teloxide-core/" # FIXME: add a simple readme for teloxide-macros #readme = "README.md" - [lib] proc-macro = true - [dependencies] quote = "1.0.7" proc-macro2 = "1.0.19" syn = { version = "1.0.13", features = ["full"] } heck = "0.4.0" + +[package.metadata.release] +tag-prefix = "macros-" +pre-release-replacements = [ + {file="CHANGELOG.md", search="## unreleased", replace="## unreleased\n\n## {{version}} - {{date}}", exactly=1}, +] diff --git a/crates/teloxide/Cargo.toml b/crates/teloxide/Cargo.toml index 92703ecd..77971857 100644 --- a/crates/teloxide/Cargo.toml +++ b/crates/teloxide/Cargo.toml @@ -127,6 +127,13 @@ rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "dep_docsrs"] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] +[package.metadata.release] +tag-prefix = "" +enable-features = ["full"] +pre-release-replacements = [ + {file="../../README.md", search="teloxide = \\{ version = \"[^\"]+\"", replace="teloxide = { version = \"{{version}}\""}, + {file="../../CHANGELOG.md", search="## unreleased", replace="## unreleased\n\n## {{version}} - {{date}}", exactly=1}, +] [[test]] name = "redis" From 835f31be80cc9c40c46bbcd1f3192493c311ea14 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 15 Sep 2023 14:21:52 +0300 Subject: [PATCH 02/26] Implemend command_separator attr to split command and args Signed-off-by: Roman Proskuryakov --- crates/teloxide-macros/src/bot_commands.rs | 5 ++-- crates/teloxide-macros/src/command.rs | 2 ++ crates/teloxide-macros/src/command_attr.rs | 5 ++++ crates/teloxide-macros/src/command_enum.rs | 16 ++++++++-- crates/teloxide/src/utils/command.rs | 24 +++++++++++++++ crates/teloxide/tests/command.rs | 34 ++++++++++++++++++++++ 6 files changed, 82 insertions(+), 4 deletions(-) diff --git a/crates/teloxide-macros/src/bot_commands.rs b/crates/teloxide-macros/src/bot_commands.rs index e081d680..6cebfdeb 100644 --- a/crates/teloxide-macros/src/bot_commands.rs +++ b/crates/teloxide-macros/src/bot_commands.rs @@ -28,7 +28,7 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result { let type_name = &input.ident; let fn_descriptions = impl_descriptions(&var_info, &command_enum); - let fn_parse = impl_parse(&var_info, &var_init); + let fn_parse = impl_parse(&var_info, &var_init, &command_enum.command_separator); let fn_commands = impl_commands(&var_info); let trait_impl = quote! { @@ -99,6 +99,7 @@ fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::To fn impl_parse( infos: &[Command], variants_initialization: &[proc_macro2::TokenStream], + command_separator: &str, ) -> proc_macro2::TokenStream { let matching_values = infos.iter().map(|c| c.get_prefixed_command()); @@ -110,7 +111,7 @@ fn impl_parse( // 2 is used to only split once (=> in two parts), // we only need to split the command and the rest of arguments. - let mut words = s.splitn(2, ' '); + let mut words = s.splitn(2, #command_separator); // Unwrap: split iterators always have at least one item let mut full_command = words.next().unwrap().split('@'); diff --git a/crates/teloxide-macros/src/command.rs b/crates/teloxide-macros/src/command.rs index a36f91eb..794d42d8 100644 --- a/crates/teloxide-macros/src/command.rs +++ b/crates/teloxide-macros/src/command.rs @@ -34,6 +34,8 @@ impl Command { parser, // FIXME: error on/do not ignore separator separator: _, + // FIXME: error on/do not ignore command separator + command_separator: _, hide, } = attrs; diff --git a/crates/teloxide-macros/src/command_attr.rs b/crates/teloxide-macros/src/command_attr.rs index b9ce1727..9d1e9550 100644 --- a/crates/teloxide-macros/src/command_attr.rs +++ b/crates/teloxide-macros/src/command_attr.rs @@ -22,6 +22,7 @@ pub(crate) struct CommandAttrs { pub rename: Option<(String, Span)>, pub parser: Option<(ParserType, Span)>, pub separator: Option<(String, Span)>, + pub command_separator: Option<(String, Span)>, pub hide: Option<((), Span)>, } @@ -48,6 +49,7 @@ enum CommandAttrKind { Rename(String), ParseWith(ParserType), Separator(String), + CommandSeparator(String), Hide, } @@ -66,6 +68,7 @@ impl CommandAttrs { rename: None, parser: None, separator: None, + command_separator: None, hide: None, }, |mut this, attr| { @@ -110,6 +113,7 @@ impl CommandAttrs { Rename(r) => insert(&mut this.rename, r, attr.sp), ParseWith(p) => insert(&mut this.parser, p, attr.sp), Separator(s) => insert(&mut this.separator, s, attr.sp), + CommandSeparator(s) => insert(&mut this.command_separator, s, attr.sp), Hide => insert(&mut this.hide, (), attr.sp), }?; @@ -165,6 +169,7 @@ impl CommandAttr { "rename" => Rename(value.expect_string()?), "parse_with" => ParseWith(ParserType::parse(value)?), "separator" => Separator(value.expect_string()?), + "command_separator" => CommandSeparator(value.expect_string()?), "hide" => value.expect_none("hide").map(|_| Hide)?, _ => { return Err(compile_error_at( diff --git a/crates/teloxide-macros/src/command_enum.rs b/crates/teloxide-macros/src/command_enum.rs index d17247cc..ff2a4960 100644 --- a/crates/teloxide-macros/src/command_enum.rs +++ b/crates/teloxide-macros/src/command_enum.rs @@ -7,6 +7,7 @@ pub(crate) struct CommandEnum { pub prefix: String, /// The bool is true if the description contains a doc comment pub description: Option<(String, bool)>, + pub command_separator: String, pub rename_rule: RenameRule, pub parser_type: ParserType, } @@ -14,8 +15,16 @@ pub(crate) struct CommandEnum { impl CommandEnum { pub fn from_attributes(attributes: &[syn::Attribute]) -> Result { let attrs = CommandAttrs::from_attributes(attributes)?; - let CommandAttrs { prefix, description, rename_rule, rename, parser, separator, hide } = - attrs; + let CommandAttrs { + prefix, + description, + rename_rule, + rename, + parser, + separator, + command_separator, + hide, + } = attrs; if let Some((_rename, sp)) = rename { return Err(compile_error_at( @@ -39,6 +48,9 @@ impl CommandEnum { Ok(Self { prefix: prefix.map(|(p, _)| p).unwrap_or_else(|| "/".to_owned()), description: description.map(|(d, is_doc, _)| (d, is_doc)), + command_separator: command_separator + .map(|(s, _)| s) + .unwrap_or_else(|| String::from(" ")), rename_rule: rename_rule.map(|(rr, _)| rr).unwrap_or(RenameRule::Identity), parser_type: parser, }) diff --git a/crates/teloxide/src/utils/command.rs b/crates/teloxide/src/utils/command.rs index d3413e9d..79c9dc7a 100644 --- a/crates/teloxide/src/utils/command.rs +++ b/crates/teloxide/src/utils/command.rs @@ -155,6 +155,30 @@ pub use teloxide_macros::BotCommands; /// # } /// ``` /// +/// 6. `#[command(command_separator = "sep")]` +/// Specify separator between command and args. Default is a space character. +/// +/// ## Example +/// ``` +/// # #[cfg(feature = "macros")] { +/// use teloxide::utils::command::BotCommands; +/// +/// #[derive(BotCommands, PartialEq, Debug)] +/// #[command( +/// rename_rule = "lowercase", +/// parse_with = "split", +/// separator = "_", +/// command_separator = "_" +/// )] +/// enum Command { +/// Nums(u8, u16, i32), +/// } +/// +/// let command = Command::parse("/nums_1_32_5", "").unwrap(); +/// assert_eq!(command, Command::Nums(1, 32, 5)); +/// # } +/// ``` +/// /// # Variant attributes /// All variant attributes override the corresponding `enum` attributes. /// diff --git a/crates/teloxide/tests/command.rs b/crates/teloxide/tests/command.rs index 765cd969..4ddbd8ba 100644 --- a/crates/teloxide/tests/command.rs +++ b/crates/teloxide/tests/command.rs @@ -166,6 +166,40 @@ fn parse_with_split4() { assert_eq!(DefaultCommands::Start(), DefaultCommands::parse("/start", "").unwrap(),); } +#[test] +#[cfg(feature = "macros")] +fn parse_with_command_separator1() { + #[derive(BotCommands, Debug, PartialEq)] + #[command(rename_rule = "lowercase")] + #[command(parse_with = "split", separator = "|", command_separator = "_")] + enum DefaultCommands { + Start(u8, String), + Help, + } + + assert_eq!( + DefaultCommands::Start(10, "hello".to_string()), + DefaultCommands::parse("/start_10|hello", "").unwrap() + ); +} + +#[test] +#[cfg(feature = "macros")] +fn parse_with_command_separator2() { + #[derive(BotCommands, Debug, PartialEq)] + #[command(rename_rule = "lowercase")] + #[command(parse_with = "split", separator = "_", command_separator = "_")] + enum DefaultCommands { + Start(u8, String), + Help, + } + + assert_eq!( + DefaultCommands::Start(10, "hello".to_string()), + DefaultCommands::parse("/start_10_hello", "").unwrap() + ); +} + #[test] #[cfg(feature = "macros")] fn parse_custom_parser() { From e73ed1bd0eaea28ce20eeff252bb4fde070da2e8 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 15 Sep 2023 17:10:54 +0300 Subject: [PATCH 03/26] More tests --- crates/teloxide/tests/command.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/teloxide/tests/command.rs b/crates/teloxide/tests/command.rs index 4ddbd8ba..ce6ccf75 100644 --- a/crates/teloxide/tests/command.rs +++ b/crates/teloxide/tests/command.rs @@ -200,6 +200,33 @@ fn parse_with_command_separator2() { ); } +#[test] +#[cfg(feature = "macros")] +fn parse_with_command_separator3() { + #[derive(BotCommands, Debug, PartialEq)] + #[command(rename_rule = "lowercase")] + #[command(parse_with = "split", command_separator = ":")] + enum DefaultCommands { + Help, + } + + assert_eq!(DefaultCommands::Help, DefaultCommands::parse("/help", "").unwrap()); +} + +#[test] +#[cfg(feature = "macros")] +fn parse_with_command_separator4() { + #[derive(BotCommands, Debug, PartialEq)] + #[command(rename_rule = "lowercase")] + #[command(parse_with = "split", command_separator = ":")] + enum DefaultCommands { + Start(u8), + Help, + } + + assert_eq!(DefaultCommands::Start(10), DefaultCommands::parse("/start:10", "").unwrap()); +} + #[test] #[cfg(feature = "macros")] fn parse_custom_parser() { From 254002619baf73111e78be0eb5599f0d8301468c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 15 Sep 2023 22:12:52 +0300 Subject: [PATCH 04/26] Add changelog --- crates/teloxide-macros/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/teloxide-macros/CHANGELOG.md b/crates/teloxide-macros/CHANGELOG.md index d9a191ef..29c5f10b 100644 --- a/crates/teloxide-macros/CHANGELOG.md +++ b/crates/teloxide-macros/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased +### Added + +- Now you can use `#[command(command_separator="sep")]` (default is a whitespace character) to set the separator between command and its arguments (issue [https://github.com/teloxide/teloxide/issues/897]) + ### Fixed - Fix `split` parser for tuple variants with len < 2 ([issue #834](https://github.com/teloxide/teloxide/issues/834)) From 836080523fc85a1ad95388b18be71cefc3083970 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 19:25:31 +0400 Subject: [PATCH 05/26] Add a bit of `trace!` logging to polling for easier debug --- crates/teloxide/src/update_listeners/polling.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/teloxide/src/update_listeners/polling.rs b/crates/teloxide/src/update_listeners/polling.rs index 3695a150..23a3c527 100644 --- a/crates/teloxide/src/update_listeners/polling.rs +++ b/crates/teloxide/src/update_listeners/polling.rs @@ -333,6 +333,7 @@ impl Stream for PollingStream<'_, B> { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { + log::trace!("polling polling stream"); let mut this = self.as_mut().project(); if *this.force_stop { @@ -342,6 +343,7 @@ impl Stream for PollingStream<'_, B> { // Poll in-flight future until completion if let Some(in_flight) = this.in_flight.as_mut().as_pin_mut() { let res = ready!(in_flight.poll(cx)); + log::trace!("in-flight completed"); this.in_flight.set(None); match res { @@ -380,7 +382,10 @@ impl Stream for PollingStream<'_, B> { // // When stopping we set `timeout = 0` and `limit = 1` so that `get_updates()` // set last seen update (offset) and return immediately - (true, _) => (*this.offset, Some(1), Some(0)), + (true, _) => { + log::trace!("graceful shutdown `get_updates` call"); + (*this.offset, Some(1), Some(0)) + } // Drop pending updates (_, true) => (-1, Some(1), Some(0)), }; From 07e08bef6ccf2293f1950042f5be8620fd8d2cd8 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 20:02:57 +0400 Subject: [PATCH 06/26] Don't use timeout to check `ShutdownToken` --- crates/teloxide/src/dispatching/dispatcher.rs | 22 ++++++--------- crates/teloxide/src/utils/shutdown_token.rs | 27 ++++++++++--------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/crates/teloxide/src/dispatching/dispatcher.rs b/crates/teloxide/src/dispatching/dispatcher.rs index d7c14387..07e80cc9 100644 --- a/crates/teloxide/src/dispatching/dispatcher.rs +++ b/crates/teloxide/src/dispatching/dispatcher.rs @@ -7,12 +7,10 @@ use crate::{ requests::{Request, Requester}, types::{Update, UpdateKind}, update_listeners::{self, UpdateListener}, - utils::shutdown_token::shutdown_check_timeout_for, }; use dptree::di::{DependencyMap, DependencySupplier}; use futures::{future::BoxFuture, stream::FuturesUnordered, StreamExt}; -use tokio::time::timeout; use tokio_stream::wrappers::ReceiverStream; use std::{ @@ -312,7 +310,6 @@ where log::debug!("hinting allowed updates: {:?}", allowed_updates); update_listener.hint_allowed_updates(&mut allowed_updates.into_iter()); - let shutdown_check_timeout = shutdown_check_timeout_for(&update_listener); let mut stop_token = Some(update_listener.stop_token()); self.state.start_dispatching(); @@ -324,19 +321,16 @@ where loop { self.remove_inactive_workers_if_needed().await; - // False positive - #[allow(clippy::collapsible_match)] - if let Ok(upd) = timeout(shutdown_check_timeout, stream.next()).await { - match upd { + tokio::select! { + upd = stream.next() => match upd { None => break, Some(upd) => self.process_update(upd, &update_listener_error_handler).await, - } - } - - if self.state.is_shutting_down() { - if let Some(token) = stop_token.take() { - log::debug!("Start shutting down dispatching..."); - token.stop(); + }, + () = self.state.wait_for_changes() => if self.state.is_shutting_down() { + if let Some(token) = stop_token.take() { + log::debug!("Start shutting down dispatching..."); + token.stop(); + } } } } diff --git a/crates/teloxide/src/utils/shutdown_token.rs b/crates/teloxide/src/utils/shutdown_token.rs index cb4da020..14ca622f 100644 --- a/crates/teloxide/src/utils/shutdown_token.rs +++ b/crates/teloxide/src/utils/shutdown_token.rs @@ -5,18 +5,16 @@ use std::{ atomic::{AtomicU8, Ordering}, Arc, }, - time::Duration, }; use tokio::sync::Notify; -use crate::update_listeners::UpdateListener; - /// A token which used to shutdown [`Dispatcher`]. /// /// [`Dispatcher`]: crate::dispatching::Dispatcher #[derive(Clone)] pub struct ShutdownToken { + // FIXME: use a single arc dispatcher_state: Arc, shutdown_notify_back: Arc, } @@ -49,11 +47,16 @@ impl ShutdownToken { Self { dispatcher_state: Arc::new(DispatcherState { inner: AtomicU8::new(ShutdownState::Idle as _), + notify: <_>::default(), }), shutdown_notify_back: <_>::default(), } } + pub(crate) async fn wait_for_changes(&self) { + self.dispatcher_state.notify.notified().await; + } + pub(crate) fn start_dispatching(&self) { if let Err(actual) = self.dispatcher_state.compare_exchange(ShutdownState::Idle, ShutdownState::Running) @@ -93,27 +96,20 @@ impl fmt::Display for IdleShutdownError { impl std::error::Error for IdleShutdownError {} -pub(crate) fn shutdown_check_timeout_for(update_listener: &impl UpdateListener) -> Duration { - const MIN_SHUTDOWN_CHECK_TIMEOUT: Duration = Duration::from_secs(1); - const DZERO: Duration = Duration::ZERO; - - let shutdown_check_timeout = update_listener.timeout_hint().unwrap_or(DZERO); - shutdown_check_timeout.saturating_add(MIN_SHUTDOWN_CHECK_TIMEOUT) -} - struct DispatcherState { inner: AtomicU8, + notify: Notify, } impl DispatcherState { // Ordering::Relaxed: only one atomic variable, nothing to synchronize. - fn load(&self) -> ShutdownState { ShutdownState::from_u8(self.inner.load(Ordering::Relaxed)) } fn store(&self, new: ShutdownState) { - self.inner.store(new as _, Ordering::Relaxed) + self.inner.store(new as _, Ordering::Relaxed); + self.notify.notify_waiters(); } fn compare_exchange( @@ -125,6 +121,11 @@ impl DispatcherState { .compare_exchange(current as _, new as _, Ordering::Relaxed, Ordering::Relaxed) .map(ShutdownState::from_u8) .map_err(ShutdownState::from_u8) + // FIXME: `Result::inspect` when :( + .map(|st| { + self.notify.notify_waiters(); + st + }) } } From d3c69eb0c920d136b63041586e4b1551d7a15e00 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 20:17:28 +0400 Subject: [PATCH 07/26] Remove `UpdateListener::timeout_hint` --- crates/teloxide/src/update_listeners.rs | 15 ---------- .../teloxide/src/update_listeners/polling.rs | 4 --- .../src/update_listeners/stateful_listener.rs | 29 +++++-------------- 3 files changed, 7 insertions(+), 41 deletions(-) diff --git a/crates/teloxide/src/update_listeners.rs b/crates/teloxide/src/update_listeners.rs index 5594d949..6e03ef01 100644 --- a/crates/teloxide/src/update_listeners.rs +++ b/crates/teloxide/src/update_listeners.rs @@ -32,8 +32,6 @@ pub mod webhooks; use futures::Stream; -use std::time::Duration; - use crate::{ stop::StopToken, types::{AllowedUpdate, Update}, @@ -94,19 +92,6 @@ pub trait UpdateListener: fn hint_allowed_updates(&mut self, hint: &mut dyn Iterator) { let _ = hint; } - - /// The timeout duration hint. - /// - /// This hints how often dispatcher should check for a shutdown. E.g., for - /// [`polling()`] this returns the [`timeout`]. - /// - /// [`timeout`]: crate::payloads::GetUpdates::timeout - /// - /// If you are implementing this trait and not sure what to return from this - /// function, just leave it with the default implementation. - fn timeout_hint(&self) -> Option { - None - } } /// [`UpdateListener`]'s supertrait/extension. diff --git a/crates/teloxide/src/update_listeners/polling.rs b/crates/teloxide/src/update_listeners/polling.rs index 23a3c527..f055121a 100644 --- a/crates/teloxide/src/update_listeners/polling.rs +++ b/crates/teloxide/src/update_listeners/polling.rs @@ -301,10 +301,6 @@ impl UpdateListener for Polling { // before self.allowed_updates = Some(hint.collect()); } - - fn timeout_hint(&self) -> Option { - self.timeout - } } impl<'a, B: Requester + Send + 'a> AsUpdateStream<'a> for Polling { diff --git a/crates/teloxide/src/update_listeners/stateful_listener.rs b/crates/teloxide/src/update_listeners/stateful_listener.rs index 2cbca5cf..87ae492a 100644 --- a/crates/teloxide/src/update_listeners/stateful_listener.rs +++ b/crates/teloxide/src/update_listeners/stateful_listener.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use futures::Stream; use crate::{ @@ -17,7 +15,7 @@ use crate::{ /// /// [`polling`]: crate::update_listeners::polling() #[non_exhaustive] -pub struct StatefulListener { +pub struct StatefulListener { /// The state of the listener. pub state: St, @@ -36,38 +34,30 @@ pub struct StatefulListener { /// Must implement `FnMut(&mut St, &mut dyn Iterator)`. pub hint_allowed_updates: Option, - - /// The function used as [`UpdateListener::timeout_hint`]. - /// - /// Must implement `Fn(&St) -> Option`. - pub timeout_hint: Option, } type Haufn = for<'a, 'b> fn(&'a mut State, &'b mut dyn Iterator); -type Thfn = for<'a> fn(&'a State) -> Option; -impl StatefulListener, Thfn> { +impl StatefulListener> { /// Creates a new stateful listener from its components. pub fn new(state: St, stream: Assf, stop_token: Sf) -> Self { - Self::new_with_hints(state, stream, stop_token, None, None) + Self::new_with_hints(state, stream, stop_token, None) } } -impl StatefulListener { +impl StatefulListener { /// Creates a new stateful listener from its components. pub fn new_with_hints( state: St, stream: Assf, stop_token: Sf, hint_allowed_updates: Option, - timeout_hint: Option, ) -> Self { - Self { state, stream, stop_token, hint_allowed_updates, timeout_hint } + Self { state, stream, stop_token, hint_allowed_updates } } } -impl<'a, St, Assf, Sf, Hauf, Thf, Strm, E> AsUpdateStream<'a> - for StatefulListener +impl<'a, St, Assf, Sf, Hauf, Strm, E> AsUpdateStream<'a> for StatefulListener where (St, Strm): 'a, Strm: Send, @@ -82,12 +72,11 @@ where } } -impl UpdateListener for StatefulListener +impl UpdateListener for StatefulListener where Self: for<'a> AsUpdateStream<'a, StreamErr = E>, Sf: FnMut(&mut St) -> StopToken, Hauf: FnMut(&mut St, &mut dyn Iterator), - Thf: Fn(&St) -> Option, { type Err = E; @@ -100,8 +89,4 @@ where f(&mut self.state, hint); } } - - fn timeout_hint(&self) -> Option { - self.timeout_hint.as_ref().and_then(|f| f(&self.state)) - } } From 808a0cf4fbc32465263812e03a6f5ba1dfb63fff Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 20:31:19 +0400 Subject: [PATCH 08/26] Drop in-flight requests when polling is asked to stop --- .../teloxide/src/update_listeners/polling.rs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/teloxide/src/update_listeners/polling.rs b/crates/teloxide/src/update_listeners/polling.rs index f055121a..644098b8 100644 --- a/crates/teloxide/src/update_listeners/polling.rs +++ b/crates/teloxide/src/update_listeners/polling.rs @@ -336,10 +336,23 @@ impl Stream for PollingStream<'_, B> { return Ready(None); } + // If there are any buffered updates, return one + if let Some(upd) = this.buffer.next() { + return Ready(Some(Ok(upd))); + } + + // Check if we should stop and if so — drop in flight request, + // we don't care about updates that happened *after* we started stopping + if !*this.stopping && this.polling.flag.is_stopped() { + *this.stopping = true; + + log::trace!("dropping in-flight request"); + this.in_flight.set(None); + } // Poll in-flight future until completion - if let Some(in_flight) = this.in_flight.as_mut().as_pin_mut() { + else if let Some(in_flight) = this.in_flight.as_mut().as_pin_mut() { let res = ready!(in_flight.poll(cx)); - log::trace!("in-flight completed"); + log::trace!("in-flight request completed"); this.in_flight.set(None); match res { @@ -364,12 +377,6 @@ impl Stream for PollingStream<'_, B> { } } - // If there are any buffered updates, return one - if let Some(upd) = this.buffer.next() { - return Ready(Some(Ok(upd))); - } - - *this.stopping = this.polling.flag.is_stopped(); let (offset, limit, timeout) = match (this.stopping, this.drop_pending_updates) { // Normal `get_updates()` call (false, false) => (*this.offset, this.polling.limit, *this.timeout), From 2b7eea26796823ad32ff83f6b016dfecda5f104b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 20:45:27 +0400 Subject: [PATCH 09/26] Use waker API instead of recursing --- crates/teloxide/src/update_listeners/polling.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/teloxide/src/update_listeners/polling.rs b/crates/teloxide/src/update_listeners/polling.rs index 644098b8..b713db37 100644 --- a/crates/teloxide/src/update_listeners/polling.rs +++ b/crates/teloxide/src/update_listeners/polling.rs @@ -406,8 +406,10 @@ impl Stream for PollingStream<'_, B> { .send(); this.in_flight.set(Some(req)); - // Recurse to poll `self.in_flight` - self.poll_next(cx) + // Immediately wake up to poll `self.in_flight` + // (without this this stream becomes a zombie) + cx.waker().wake_by_ref(); + Poll::Pending } } From 6cc6c04192408a753a6759ac35cc093c17cc9d98 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 21:10:50 +0400 Subject: [PATCH 10/26] Polling: `poll` stop flag instead of checking, to make sure we wakeup immediately when stopping --- .../teloxide/src/update_listeners/polling.rs | 65 +++++++++++++++++-- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/crates/teloxide/src/update_listeners/polling.rs b/crates/teloxide/src/update_listeners/polling.rs index b713db37..62d63076 100644 --- a/crates/teloxide/src/update_listeners/polling.rs +++ b/crates/teloxide/src/update_listeners/polling.rs @@ -1,6 +1,7 @@ use std::{ convert::TryInto, future::Future, + mem, pin::Pin, task::{ self, @@ -97,8 +98,16 @@ where pub fn build(self) -> Polling { let Self { bot, timeout, limit, allowed_updates, drop_pending_updates } = self; let (token, flag) = mk_stop_token(); - let polling = - Polling { bot, timeout, limit, allowed_updates, drop_pending_updates, flag, token }; + let polling = Polling { + bot, + timeout, + limit, + allowed_updates, + drop_pending_updates, + flag: Some(flag), + token, + stop_token_cloned: false, + }; assert_update_listener(polling) } @@ -240,17 +249,21 @@ pub struct Polling { limit: Option, allowed_updates: Option>, drop_pending_updates: bool, - flag: StopFlag, + flag: Option, token: StopToken, + stop_token_cloned: bool, } impl Polling where - R: Requester + Send + 'static, - ::GetUpdates: Send, + R: Requester, { /// Returns a builder for polling update listener. - pub fn builder(bot: R) -> PollingBuilder { + pub fn builder(bot: R) -> PollingBuilder + where + R: Send + 'static, + ::GetUpdates: Send, + { PollingBuilder { bot, timeout: None, @@ -259,6 +272,19 @@ where drop_pending_updates: false, } } + + /// Returns true if re-initialization happened *and* + /// the previous token was cloned. + fn reinit_stop_flag_if_needed(&mut self) -> bool { + if self.flag.is_some() { + return false; + } + + let (token, flag) = mk_stop_token(); + self.token = token; + self.flag = Some(flag); + mem::replace(&mut self.stop_token_cloned, false) + } } #[pin_project::pin_project] @@ -287,12 +313,18 @@ pub struct PollingStream<'a, B: Requester> { /// In-flight `get_updates()` call. #[pin] in_flight: Option<::Send>, + + /// The flag that notifies polling to stop polling. + #[pin] + flag: StopFlag, } impl UpdateListener for Polling { type Err = B::Err; fn stop_token(&mut self) -> StopToken { + self.reinit_stop_flag_if_needed(); + self.stop_token_cloned = true; self.token.clone() } @@ -311,6 +343,21 @@ impl<'a, B: Requester + Send + 'a> AsUpdateStream<'a> for Polling { let timeout = self.timeout.map(|t| t.as_secs().try_into().expect("timeout is too big")); let allowed_updates = self.allowed_updates.clone(); let drop_pending_updates = self.drop_pending_updates; + + let token_used_and_updated = self.reinit_stop_flag_if_needed(); + + // FIXME: document that `as_stream` is a destructive operation, actually, + // and you need to call `stop_token` *again* after it + if token_used_and_updated { + panic!( + "detected calling `as_stream` a second time after calling `stop_token`. \ + `as_stream` updates the stop token, thus you need to call it again after calling \ + `as_stream`" + ) + } + + // Unwrap: just called reinit + let flag = self.flag.take().unwrap(); PollingStream { polling: self, drop_pending_updates, @@ -321,6 +368,7 @@ impl<'a, B: Requester + Send + 'a> AsUpdateStream<'a> for Polling { stopping: false, buffer: Vec::new().into_iter(), in_flight: None, + flag, } } } @@ -343,7 +391,10 @@ impl Stream for PollingStream<'_, B> { // Check if we should stop and if so — drop in flight request, // we don't care about updates that happened *after* we started stopping - if !*this.stopping && this.polling.flag.is_stopped() { + // + // N.B.: it's important to use `poll` and not `is_stopped` here, + // so that *this stream* is polled when the flag is set to stop + if !*this.stopping && matches!(this.flag.poll(cx), Poll::Ready(())) { *this.stopping = true; log::trace!("dropping in-flight request"); From 035cdc69302a2dbe293617f9a36b2ac71b68f43e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 25 Sep 2023 21:54:44 +0400 Subject: [PATCH 11/26] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76bf5e0e..f0d94026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `MessageToCopyNotFound` error to `teloxide::errors::ApiError` ([PR 917](https://github.com/teloxide/teloxide/pull/917)) ### Fixed - Use `UserId` instead of `i64` for `user_id` in `html::user_mention` and `markdown::user_mention` ([PR 896](https://github.com/teloxide/teloxide/pull/896)) +- Greatly improved the speed of graceful shutdown (`^C`) ([PR 938](https://github.com/teloxide/teloxide/pull/938)) + +### Removed + +- `UpdateListener::timeout_hint` and related APIs ([PR 938](https://github.com/teloxide/teloxide/pull/938)) ## 0.12.2 - 2023-02-15 From 53f3e3a92f09541f6743830e9637e719ae23a204 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Mon, 25 Sep 2023 23:28:04 +0400 Subject: [PATCH 12/26] Fixup changelog a bit --- crates/teloxide-macros/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/teloxide-macros/CHANGELOG.md b/crates/teloxide-macros/CHANGELOG.md index 29c5f10b..57b65163 100644 --- a/crates/teloxide-macros/CHANGELOG.md +++ b/crates/teloxide-macros/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Now you can use `#[command(command_separator="sep")]` (default is a whitespace character) to set the separator between command and its arguments (issue [https://github.com/teloxide/teloxide/issues/897]) +- Now you can use `#[command(command_separator="sep")]` (default is a whitespace character) to set the separator between command and its arguments ([issue #897](https://github.com/teloxide/teloxide/issues/897)) ### Fixed From d178b4ab02f1b4c571bfc3754cc1c8b1a7d278e1 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 26 Sep 2023 04:20:41 +0400 Subject: [PATCH 13/26] Configure triagebot --- triagebot.toml | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 triagebot.toml diff --git a/triagebot.toml b/triagebot.toml new file mode 100644 index 00000000..4ca62d22 --- /dev/null +++ b/triagebot.toml @@ -0,0 +1,59 @@ +[assign] +warn_non_default_branch = true +# contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html" # FIXME: configure + +[assign.adhoc_groups] +# This is a special group that will be used if none of the `owners` entries matches. +fallback = ["@WaffleLapkin", "@Hirrolot"] + +[assign.owners] +"crates/teloxide-core" = ["@WaffleLapkin"] +"crates/teloxide-macros" = ["@WaffleLapkin"] +".github" = ["@WaffleLapkin"] + + +[autolabel."S-waiting-on-review"] +new_pr = true + +#[autolabel."new-issue"] +#new_issue = true + +[autolabel."C-core"] +trigger_files = ["crates/teloxide-core"] + +[autolabel."C-main"] +trigger_files = ["crates/teloxide"] + +[autolabel."C-macros"] +trigger_files = ["crates/teloxide-macros"] + + +[relabel] +allow-unauthenticated = [ + "S-*", # Status + "C-*", # Crate + "breaking change", + "bug", + "documentation", + "duplicate", + "feature-request", + "FIXME", + "frozen", + "proposal", + "question", + "tba-update", + "Unknown API error", + "WIP", +] + + +# https://forge.rust-lang.org/triagebot/github-releases.html? + + +[review-submitted] +# This label is added when a review is submitted. +reviewed_label = "S-waiting-on-author" +# These labels are removed when a review is submitted. +review_labels = ["S-waiting-on-review"] + +[shortcut] From 0a995fec61bd9236e2f6a4902dae11d7ce28e316 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 26 Sep 2023 16:46:03 +0400 Subject: [PATCH 14/26] Make Hirrolot codeowner of `crates/teloxide` --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 4ca62d22..1a9f3b3f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -9,6 +9,7 @@ fallback = ["@WaffleLapkin", "@Hirrolot"] [assign.owners] "crates/teloxide-core" = ["@WaffleLapkin"] "crates/teloxide-macros" = ["@WaffleLapkin"] +"crates/teloxide" = ["@Hirrolot"] ".github" = ["@WaffleLapkin"] From 04df142191473c23f467dca5f9567d223b5e1e0a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Sep 2023 12:27:30 +0400 Subject: [PATCH 15/26] Update nightly rust used for development and CI --- .github/workflows/ci.yml | 4 ++-- crates/teloxide-core/src/codegen.rs | 4 ++-- rust-toolchain.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18852b28..5f6d1d79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: # When updating this, also update: # - crates/teloxide-core/src/codegen.rs # - rust-toolchain.toml - rust_nightly: nightly-2023-05-28 + rust_nightly: nightly-2023-09-27 # When updating this, also update: # - **/README.md # - **/src/lib.rs @@ -84,7 +84,7 @@ jobs: toolchain: beta features: "--features full" - rust: nightly - toolchain: nightly-2023-05-28 + toolchain: nightly-2023-09-27 features: "--features full nightly" - rust: msrv toolchain: 1.65.0 diff --git a/crates/teloxide-core/src/codegen.rs b/crates/teloxide-core/src/codegen.rs index afde1663..f14d3f62 100644 --- a/crates/teloxide-core/src/codegen.rs +++ b/crates/teloxide-core/src/codegen.rs @@ -23,7 +23,7 @@ use xshell::{cmd, Shell}; fn ensure_rustfmt(sh: &Shell) { // FIXME(waffle): find a better way to set toolchain - let toolchain = "nightly-2023-05-28"; + let toolchain = "nightly-2023-09-27"; let version = cmd!(sh, "rustup run {toolchain} rustfmt --version").read().unwrap_or_default(); @@ -36,7 +36,7 @@ fn ensure_rustfmt(sh: &Shell) { } pub fn reformat(text: String) -> String { - let toolchain = "nightly-2023-05-28"; + let toolchain = "nightly-2023-09-27"; let sh = Shell::new().unwrap(); ensure_rustfmt(&sh); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c58ff624..0c8b465f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2023-05-28" +channel = "nightly-2023-09-27" components = ["rustfmt", "clippy"] profile = "minimal" From 557a7c0df7832337d7cc61188dd17b625b4953bf Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Sep 2023 12:29:28 +0400 Subject: [PATCH 16/26] re-fmt --- .../teloxide-core/src/adaptors/throttle/worker.rs | 14 +++++++------- crates/teloxide-macros/src/attr.rs | 5 +---- crates/teloxide-macros/src/command_attr.rs | 13 ++++++------- crates/teloxide/src/repls/commands_repl.rs | 10 +++++----- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/crates/teloxide-core/src/adaptors/throttle/worker.rs b/crates/teloxide-core/src/adaptors/throttle/worker.rs index 7aa42692..67e7ecf3 100644 --- a/crates/teloxide-core/src/adaptors/throttle/worker.rs +++ b/crates/teloxide-core/src/adaptors/throttle/worker.rs @@ -155,18 +155,18 @@ pub(super) async fn worker( // // Reasons (not to use `spawn_blocking`): // - // 1. The work seems not very CPU-bound, it's not heavy computations, - // it's more like light computations. + // 1. The work seems not very CPU-bound, it's not heavy computations, it's more + // like light computations. // // 2. `spawn_blocking` is not zero-cost — it spawns a new system thread // + do so other work. This may actually be *worse* then current // "just do everything in this async fn" approach. // - // 3. With `rt-threaded` feature, tokio uses [`num_cpus()`] threads - // which should be enough to work fine with one a-bit-blocking task. - // Crucially current behaviour will be problem mostly with - // single-threaded runtimes (and in case you're using one, you - // probably don't want to spawn unnecessary threads anyway). + // 3. With `rt-threaded` feature, tokio uses [`num_cpus()`] threads which should + // be enough to work fine with one a-bit-blocking task. Crucially current + // behaviour will be problem mostly with single-threaded runtimes (and in + // case you're using one, you probably don't want to spawn unnecessary + // threads anyway). // // I think if we'll ever change this behaviour, we need to make it // _configurable_. diff --git a/crates/teloxide-macros/src/attr.rs b/crates/teloxide-macros/src/attr.rs index bc3e7dd3..c581b91c 100644 --- a/crates/teloxide-macros/src/attr.rs +++ b/crates/teloxide-macros/src/attr.rs @@ -19,10 +19,7 @@ pub(crate) fn fold_attrs( .filter(|&a| filter(a)) .flat_map(|attribute| { let Some(key) = attribute.path.get_ident().cloned() else { - return vec![Err(compile_error_at( - "expected an ident", - attribute.path.span(), - ))]; + return vec![Err(compile_error_at("expected an ident", attribute.path.span()))]; }; match (|input: ParseStream<'_>| Attrs::parse_with_key(input, key)) diff --git a/crates/teloxide-macros/src/command_attr.rs b/crates/teloxide-macros/src/command_attr.rs index 9d1e9550..91f87bc9 100644 --- a/crates/teloxide-macros/src/command_attr.rs +++ b/crates/teloxide-macros/src/command_attr.rs @@ -145,13 +145,12 @@ impl CommandAttr { } "command" => { - let Some(attr) = key.pop() - else { - return Err(compile_error_at( - "expected an attribute name", - outermost_key.span(), - )) - }; + let Some(attr) = key.pop() else { + return Err(compile_error_at( + "expected an attribute name", + outermost_key.span(), + )); + }; if let Some(unexpected_key) = key.last() { return Err(compile_error_at( diff --git a/crates/teloxide/src/repls/commands_repl.rs b/crates/teloxide/src/repls/commands_repl.rs index 68093d8f..c846089e 100644 --- a/crates/teloxide/src/repls/commands_repl.rs +++ b/crates/teloxide/src/repls/commands_repl.rs @@ -165,9 +165,9 @@ where /// the [`Requester`] trait. /// 2. `handler` is an `async` function that takes arguments from /// [`DependencyMap`] (see below) and returns [`ResponseResult`]. -/// 3. `cmd` is a type hint for your command enumeration -/// `MyCommand`: just write `MyCommand::ty()`. Note that `MyCommand` must -/// implement the [`BotCommands`] trait, typically via +/// 3. `cmd` is a type hint for your command enumeration `MyCommand`: just write +/// `MyCommand::ty()`. Note that `MyCommand` must implement the +/// [`BotCommands`] trait, typically via /// `#[derive(BotCommands)]`. /// /// All the other requirements are about thread safety and data validity and can @@ -236,8 +236,8 @@ where /// [`DependencyMap`] (see below) and returns [`ResponseResult`]. /// 3. `listener` is something that takes updates from a Telegram server and /// implements [`UpdateListener`]. -/// 4. `cmd` is a type hint for your command enumeration `MyCommand`: just -/// write `MyCommand::ty()`. Note that `MyCommand` must implement the +/// 4. `cmd` is a type hint for your command enumeration `MyCommand`: just write +/// `MyCommand::ty()`. Note that `MyCommand` must implement the /// [`BotCommands`] trait, typically via `#[derive(BotCommands)]`. /// /// All the other requirements are about thread safety and data validity and can From 3720296eea1e55e1755b8e7b3a357e21782cf211 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Sep 2023 12:31:13 +0400 Subject: [PATCH 17/26] Specify `resolver = "2"` in `Cargo.toml` it's the default for edition 2021, but somehow not the default for the workspace... --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a929940a..1c176a1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = ["crates/*"] - +resolver = "2" # The settings below will be applied to all crates in the workspace [workspace.package] From 4b508966a20eb51b3822fc56c927bd3ee4c6e0ad Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Sep 2023 12:34:44 +0400 Subject: [PATCH 18/26] Update `proc-macro2` to fix nightly issue --- crates/teloxide-macros/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/teloxide-macros/Cargo.toml b/crates/teloxide-macros/Cargo.toml index b536ce16..95cfb5a2 100644 --- a/crates/teloxide-macros/Cargo.toml +++ b/crates/teloxide-macros/Cargo.toml @@ -18,12 +18,12 @@ proc-macro = true [dependencies] quote = "1.0.7" -proc-macro2 = "1.0.19" -syn = { version = "1.0.13", features = ["full"] } +proc-macro2 = "1.0.67" +syn = { version = "1.0.13", features = ["full"] } heck = "0.4.0" [package.metadata.release] tag-prefix = "macros-" pre-release-replacements = [ - {file="CHANGELOG.md", search="## unreleased", replace="## unreleased\n\n## {{version}} - {{date}}", exactly=1}, + { file = "CHANGELOG.md", search = "## unreleased", replace = "## unreleased\n\n## {{version}} - {{date}}", exactly = 1 }, ] From 729484af4c1405b417bf0ec154857160a77eca3e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Sep 2023 12:47:38 +0400 Subject: [PATCH 19/26] satisfy clippy --- crates/teloxide-core/src/local_macros.rs | 2 ++ crates/teloxide-core/src/payloads/codegen.rs | 5 ++++- crates/teloxide-core/src/requests/requester.rs | 2 ++ crates/teloxide-core/src/types.rs | 2 +- crates/teloxide-core/src/types/passport_element_error.rs | 4 ++-- crates/teloxide-core/src/types/recipient.rs | 2 +- crates/teloxide-core/src/types/thread_id.rs | 4 ++-- .../src/dispatching/dialogue/storage/sqlite_storage.rs | 8 ++++---- crates/teloxide/src/utils/markdown.rs | 5 +---- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/crates/teloxide-core/src/local_macros.rs b/crates/teloxide-core/src/local_macros.rs index 6b66dfe4..913c1a81 100644 --- a/crates/teloxide-core/src/local_macros.rs +++ b/crates/teloxide-core/src/local_macros.rs @@ -1333,6 +1333,8 @@ macro_rules! requester_forward { } #[test] +// waffle: efficiency is not important here, and I don't want to rewrite this +#[allow(clippy::format_collect)] fn codegen_requester_forward() { use crate::codegen::{ add_hidden_preamble, diff --git a/crates/teloxide-core/src/payloads/codegen.rs b/crates/teloxide-core/src/payloads/codegen.rs index 771caa7d..5c78f623 100644 --- a/crates/teloxide-core/src/payloads/codegen.rs +++ b/crates/teloxide-core/src/payloads/codegen.rs @@ -1,3 +1,6 @@ +// waffle: efficiency is not important here, and I don't want to rewrite this +#![allow(clippy::format_collect)] + use std::{borrow::Borrow, collections::HashSet, ops::Deref}; use itertools::Itertools; @@ -51,7 +54,7 @@ fn codegen_payloads() { let multipart = multipart_input_file_fields(&method) .map(|field| format!(" @[multipart = {}]\n", field.join(", "))) - .unwrap_or_else(String::new); + .unwrap_or_default(); let derive = if !multipart.is_empty() || matches!( diff --git a/crates/teloxide-core/src/requests/requester.rs b/crates/teloxide-core/src/requests/requester.rs index 5336731e..887aca6d 100644 --- a/crates/teloxide-core/src/requests/requester.rs +++ b/crates/teloxide-core/src/requests/requester.rs @@ -1337,6 +1337,8 @@ where // } #[test] +// waffle: efficiency is not important here, and I don't want to rewrite this +#[allow(clippy::format_collect)] fn codegen_requester_methods() { use crate::codegen::{ add_hidden_preamble, diff --git a/crates/teloxide-core/src/types.rs b/crates/teloxide-core/src/types.rs index 890dfb1a..0a94ce54 100644 --- a/crates/teloxide-core/src/types.rs +++ b/crates/teloxide-core/src/types.rs @@ -315,7 +315,7 @@ pub(crate) mod serde_opt_date_from_unix_timestamp { } { - let json = r#"{}"#; + let json = "{}"; let Struct { date } = serde_json::from_str(json).unwrap(); assert_eq!(date, None); diff --git a/crates/teloxide-core/src/types/passport_element_error.rs b/crates/teloxide-core/src/types/passport_element_error.rs index a3b94d0f..f6c72509 100644 --- a/crates/teloxide-core/src/types/passport_element_error.rs +++ b/crates/teloxide-core/src/types/passport_element_error.rs @@ -203,8 +203,8 @@ impl PassportElementErrorReverseSide { } } -//// Represents an issue with the selfie with a document. -// +/// Represents an issue with the selfie with a document. +/// /// The error is considered resolved when the file with the selfie changes. /// /// [The official docs](https://core.telegram.org/bots/api#passportelementerrorselfie). diff --git a/crates/teloxide-core/src/types/recipient.rs b/crates/teloxide-core/src/types/recipient.rs index 8818f559..d15f9003 100644 --- a/crates/teloxide-core/src/types/recipient.rs +++ b/crates/teloxide-core/src/types/recipient.rs @@ -31,7 +31,7 @@ mod tests { #[test] fn chat_id_id_serialization() { - let expected_json = String::from(r#"123456"#); + let expected_json = String::from("123456"); let actual_json = serde_json::to_string(&Recipient::Id(ChatId(123_456))).unwrap(); assert_eq!(expected_json, actual_json) diff --git a/crates/teloxide-core/src/types/thread_id.rs b/crates/teloxide-core/src/types/thread_id.rs index 2e3f50ac..37e55568 100644 --- a/crates/teloxide-core/src/types/thread_id.rs +++ b/crates/teloxide-core/src/types/thread_id.rs @@ -66,7 +66,7 @@ mod tests { #[test] fn smoke_deser() { - let json = r#"123"#; + let json = "123"; let mid: ThreadId = serde_json::from_str(json).unwrap(); assert_eq!(mid, ThreadId(MessageId(123))); } @@ -75,6 +75,6 @@ mod tests { fn smoke_ser() { let mid: ThreadId = ThreadId(MessageId(123)); let json = serde_json::to_string(&mid).unwrap(); - assert_eq!(json, r#"123"#); + assert_eq!(json, "123"); } } diff --git a/crates/teloxide/src/dispatching/dialogue/storage/sqlite_storage.rs b/crates/teloxide/src/dispatching/dialogue/storage/sqlite_storage.rs index 32425b24..00974be9 100644 --- a/crates/teloxide/src/dispatching/dialogue/storage/sqlite_storage.rs +++ b/crates/teloxide/src/dispatching/dialogue/storage/sqlite_storage.rs @@ -42,12 +42,12 @@ impl SqliteStorage { let pool = SqlitePool::connect(format!("sqlite:{path}?mode=rwc").as_str()).await?; let mut conn = pool.acquire().await?; sqlx::query( - r#" + " CREATE TABLE IF NOT EXISTS teloxide_dialogues ( chat_id BIGINT PRIMARY KEY, dialogue BLOB NOT NULL ); - "#, + ", ) .execute(&mut conn) .await?; @@ -98,10 +98,10 @@ where .await? .execute( sqlx::query( - r#" + " INSERT INTO teloxide_dialogues VALUES (?, ?) ON CONFLICT(chat_id) DO UPDATE SET dialogue=excluded.dialogue - "#, + ", ) .bind(chat_id) .bind(d), diff --git a/crates/teloxide/src/utils/markdown.rs b/crates/teloxide/src/utils/markdown.rs index 4e072e6b..e48b10c6 100644 --- a/crates/teloxide/src/utils/markdown.rs +++ b/crates/teloxide/src/utils/markdown.rs @@ -283,9 +283,6 @@ mod tests { is_premium: false, added_to_attachment_menu: false, }; - assert_eq!( - user_mention_or_link(&user_without_username), - r#"[Name](tg://user/?id=123456789)"# - ) + assert_eq!(user_mention_or_link(&user_without_username), "[Name](tg://user/?id=123456789)") } } From cec2a018ff7890f4ebc8a20a1ae3a593b5fec505 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 4 Oct 2023 19:37:18 +0400 Subject: [PATCH 20/26] Use `futures::future::select` instead of `tokio::select!` Pros: less macros, fixes the missing feature issue. Cons: a bit more code because `fututres` is an annoying crate which does not use `either::Either` and provides its own `Either` which does not have `map_either`, or basically anything for that matter. --- crates/teloxide-core/Cargo.toml | 2 +- .../src/adaptors/throttle/worker.rs | 34 ++++++++++++------ crates/teloxide/Cargo.toml | 1 + crates/teloxide/src/dispatching/dispatcher.rs | 35 ++++++++++++++----- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/crates/teloxide-core/Cargo.toml b/crates/teloxide-core/Cargo.toml index aebd8a0d..1b6509ac 100644 --- a/crates/teloxide-core/Cargo.toml +++ b/crates/teloxide-core/Cargo.toml @@ -31,7 +31,7 @@ native-tls = ["reqwest/native-tls"] nightly = [] # Throttling bot adaptor -throttle = ["vecrem", "tokio/macros"] +throttle = ["vecrem"] # Trace bot adaptor trace_adaptor = [] diff --git a/crates/teloxide-core/src/adaptors/throttle/worker.rs b/crates/teloxide-core/src/adaptors/throttle/worker.rs index 67e7ecf3..5e1bc116 100644 --- a/crates/teloxide-core/src/adaptors/throttle/worker.rs +++ b/crates/teloxide-core/src/adaptors/throttle/worker.rs @@ -1,8 +1,11 @@ use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, + pin::pin, time::{Duration, Instant}, }; +use either::Either; +use futures::{future, FutureExt as _}; use tokio::sync::{mpsc, mpsc::error::TryRecvError, oneshot::Sender}; use vecrem::VecExt; @@ -129,17 +132,19 @@ pub(super) async fn worker( answer_info(&mut info_rx, &mut limits); loop { - tokio::select! { - freeze_until = freeze_rx.recv() => { - freeze( - &mut freeze_rx, - slow_mode.as_mut(), - &bot, - freeze_until - ) - .await; - }, - () = read_from_rx(&mut rx, &mut queue, &mut rx_is_closed) => break, + let res = future::select( + pin!(freeze_rx.recv()), + pin!(read_from_rx(&mut rx, &mut queue, &mut rx_is_closed)), + ) + .map(either) + .await + .map_either(|l| l.0, |r| r.0); + + match res { + Either::Left(freeze_until) => { + freeze(&mut freeze_rx, slow_mode.as_mut(), &bot, freeze_until).await; + } + Either::Right(()) => break, } } //debug_assert_eq!(queue.capacity(), limits.messages_per_sec_overall as usize); @@ -372,6 +377,13 @@ async fn read_from_rx(rx: &mut mpsc::Receiver, queue: &mut Vec, rx_is_c } } +fn either(x: future::Either) -> Either { + match x { + future::Either::Left(l) => Either::Left(l), + future::Either::Right(r) => Either::Right(r), + } +} + #[cfg(test)] mod tests { #[tokio::test] diff --git a/crates/teloxide/Cargo.toml b/crates/teloxide/Cargo.toml index 77971857..d51e7e34 100644 --- a/crates/teloxide/Cargo.toml +++ b/crates/teloxide/Cargo.toml @@ -94,6 +94,7 @@ futures = "0.3.15" pin-project = "1.0" serde_with_macros = "1.4" aquamarine = "0.1.11" +either = "1.9.0" sqlx = { version = "0.6", optional = true, default-features = false, features = [ "macros", diff --git a/crates/teloxide/src/dispatching/dispatcher.rs b/crates/teloxide/src/dispatching/dispatcher.rs index 07e80cc9..4629c3fe 100644 --- a/crates/teloxide/src/dispatching/dispatcher.rs +++ b/crates/teloxide/src/dispatching/dispatcher.rs @@ -10,7 +10,12 @@ use crate::{ }; use dptree::di::{DependencyMap, DependencySupplier}; -use futures::{future::BoxFuture, stream::FuturesUnordered, StreamExt}; +use either::Either; +use futures::{ + future::{self, BoxFuture}, + stream::FuturesUnordered, + FutureExt as _, StreamExt as _, +}; use tokio_stream::wrappers::ReceiverStream; use std::{ @@ -19,6 +24,7 @@ use std::{ future::Future, hash::Hash, ops::{ControlFlow, Deref}, + pin::pin, sync::{ atomic::{AtomicBool, AtomicU32, Ordering}, Arc, @@ -321,15 +327,22 @@ where loop { self.remove_inactive_workers_if_needed().await; - tokio::select! { - upd = stream.next() => match upd { - None => break, + let res = future::select(stream.next(), pin!(self.state.wait_for_changes())) + .map(either) + .await + .map_either(|l| l.0, |r| r.0); + + match res { + Either::Left(upd) => match upd { Some(upd) => self.process_update(upd, &update_listener_error_handler).await, + None => break, }, - () = self.state.wait_for_changes() => if self.state.is_shutting_down() { - if let Some(token) = stop_token.take() { - log::debug!("Start shutting down dispatching..."); - token.stop(); + Either::Right(()) => { + if self.state.is_shutting_down() { + if let Some(token) = stop_token.take() { + log::debug!("Start shutting down dispatching..."); + token.stop(); + } } } } @@ -578,6 +591,12 @@ async fn handle_update( } } +fn either(x: future::Either) -> Either { + match x { + future::Either::Left(l) => Either::Left(l), + future::Either::Right(r) => Either::Right(r), + } +} #[cfg(test)] mod tests { use std::convert::Infallible; From e5503e6525e58ce8628ead080493ffc4cbbc8f82 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 4 Oct 2023 19:42:18 +0400 Subject: [PATCH 21/26] format cargo tomls --- crates/teloxide-core/Cargo.toml | 30 ++++++++++++++++++++++-------- crates/teloxide/Cargo.toml | 22 +++++++++++++++------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/crates/teloxide-core/Cargo.toml b/crates/teloxide-core/Cargo.toml index 1b6509ac..ab9addb7 100644 --- a/crates/teloxide-core/Cargo.toml +++ b/crates/teloxide-core/Cargo.toml @@ -55,15 +55,19 @@ tokio = { version = "1.12.0", features = ["fs"] } tokio-util = { version = "0.7.0", features = ["codec"] } pin-project = "1.0.12" 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"] } log = "0.4" serde = { version = "1.0.114", features = ["derive"] } serde_json = "1.0.55" serde_with_macros = "1.5.2" -uuid = { version = "1.1.0", features = ["v4"] } # for attaching input files - +uuid = { version = "1.1.0", features = ["v4"] } # for attaching input files + derive_more = "0.99.9" mime = "0.3.16" thiserror = "1.0.20" @@ -81,7 +85,12 @@ vecrem = { version = "0.1", optional = true } [dev-dependencies] pretty_env_logger = "0.4" -tokio = { version = "1.8.0", features = ["fs", "macros", "macros", "rt-multi-thread"] } +tokio = { version = "1.8.0", features = [ + "fs", + "macros", + "macros", + "rt-multi-thread", +] } cool_asserts = "2.0.3" xshell = "0.2" @@ -102,9 +111,9 @@ cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] tag-prefix = "core-" enable-features = ["full"] pre-release-replacements = [ - {file="README.md", search="teloxide-core = \"[^\"]+\"", replace="teloxide-core = \"{{version}}\""}, - {file="src/lib.rs", search="teloxide-core = \"[^\"]+\"", replace="teloxide-core = \"{{version}}\""}, - {file="CHANGELOG.md", search="## unreleased", replace="## unreleased\n\n## {{version}} - {{date}}", exactly=1}, + { file = "README.md", search = "teloxide-core = \"[^\"]+\"", replace = "teloxide-core = \"{{version}}\"" }, + { file = "src/lib.rs", search = "teloxide-core = \"[^\"]+\"", replace = "teloxide-core = \"{{version}}\"" }, + { file = "CHANGELOG.md", search = "## unreleased", replace = "## unreleased\n\n## {{version}} - {{date}}", exactly = 1 }, ] [[example]] @@ -118,4 +127,9 @@ doc-scrape-examples = true [[example]] name = "erased" -required-features = ["tokio/macros", "tokio/rt-multi-thread", "erased", "trace_adaptor"] +required-features = [ + "tokio/macros", + "tokio/rt-multi-thread", + "erased", + "trace_adaptor", +] diff --git a/crates/teloxide/Cargo.toml b/crates/teloxide/Cargo.toml index d51e7e34..59c0fdb7 100644 --- a/crates/teloxide/Cargo.toml +++ b/crates/teloxide/Cargo.toml @@ -4,6 +4,7 @@ version = "0.12.2" description = "An elegant Telegram bots framework for Rust" rust-version.workspace = true + edition.workspace = true license.workspace = true homepage.workspace = true @@ -17,7 +18,7 @@ categories = ["web-programming", "api-bindings", "asynchronous"] [features] -default = ["native-tls", "ctrlc_handler", "teloxide-core/default", "auto-send"] +default = ["native-tls", "ctrlc_handler", "teloxide-core/default", "auto-send"] webhooks = ["rand"] webhooks-axum = ["webhooks", "axum", "tower", "tower-http"] @@ -37,7 +38,9 @@ native-tls = ["teloxide-core/native-tls"] rustls = ["teloxide-core/rustls"] auto-send = ["teloxide-core/auto_send"] throttle = ["teloxide-core/throttle"] -cache-me = ["teloxide-core/cache_me"] # FIXME: why teloxide and core use - _ differently? +cache-me = [ + "teloxide-core/cache_me", +] # FIXME: why teloxide and core use - _ differently? trace-adaptor = ["teloxide-core/trace_adaptor"] erased = ["teloxide-core/erased"] @@ -97,8 +100,8 @@ aquamarine = "0.1.11" either = "1.9.0" sqlx = { version = "0.6", optional = true, default-features = false, features = [ - "macros", - "sqlite", + "macros", + "sqlite", ] } redis = { version = "0.21", features = ["tokio-comp"], optional = true } serde_cbor = { version = "0.11", optional = true } @@ -132,8 +135,8 @@ cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] tag-prefix = "" enable-features = ["full"] pre-release-replacements = [ - {file="../../README.md", search="teloxide = \\{ version = \"[^\"]+\"", replace="teloxide = { version = \"{{version}}\""}, - {file="../../CHANGELOG.md", search="## unreleased", replace="## unreleased\n\n## {{version}} - {{date}}", exactly=1}, + { file = "../../README.md", search = "teloxide = \\{ version = \"[^\"]+\"", replace = "teloxide = { version = \"{{version}}\"" }, + { file = "../../CHANGELOG.md", search = "## unreleased", replace = "## unreleased\n\n## {{version}} - {{date}}", exactly = 1 }, ] [[test]] @@ -166,7 +169,12 @@ required-features = ["macros", "ctrlc_handler"] [[example]] name = "db_remember" -required-features = ["sqlite-storage", "redis-storage", "bincode-serializer", "macros"] +required-features = [ + "sqlite-storage", + "redis-storage", + "bincode-serializer", + "macros", +] [[example]] name = "dialogue" From fbd5bf66cde766f168308651791a476a599b10a9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 4 Oct 2023 20:01:46 +0400 Subject: [PATCH 22/26] bump msrv --- .github/workflows/ci.yml | 6 ++++-- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- README.md | 2 +- crates/teloxide-core/CHANGELOG.md | 3 ++- crates/teloxide-core/README.md | 2 +- crates/teloxide-core/src/lib.rs | 2 +- crates/teloxide-macros/CHANGELOG.md | 8 +++++--- 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f6d1d79..de4ee898 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,9 @@ env: # - **/README.md # - **/src/lib.rs # - down below in a matrix - rust_msrv: 1.65.0 + # - `Cargo.toml` + # - **/CHANGELOG.md + rust_msrv: 1.68.0 CI: 1 @@ -87,7 +89,7 @@ jobs: toolchain: nightly-2023-09-27 features: "--features full nightly" - rust: msrv - toolchain: 1.65.0 + toolchain: 1.68.0 features: "--features full" steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index f0d94026..7dceb57f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased ### Added + - Add `MessageToCopyNotFound` error to `teloxide::errors::ApiError` ([PR 917](https://github.com/teloxide/teloxide/pull/917)) + ### Fixed + - Use `UserId` instead of `i64` for `user_id` in `html::user_mention` and `markdown::user_mention` ([PR 896](https://github.com/teloxide/teloxide/pull/896)) - Greatly improved the speed of graceful shutdown (`^C`) ([PR 938](https://github.com/teloxide/teloxide/pull/938)) +### Changed + +- MSRV (Minimal Supported Rust Version) was bumped from `1.64.0` to `1.68.0` ([PR 950][https://github.com/teloxide/teloxide/pull/950]) + ### Removed - `UpdateListener::timeout_hint` and related APIs ([PR 938](https://github.com/teloxide/teloxide/pull/938)) diff --git a/Cargo.toml b/Cargo.toml index 1c176a1f..803ed545 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ resolver = "2" # The settings below will be applied to all crates in the workspace [workspace.package] # MSRV (minimal supported Rust version). -rust-version = "1.65" +rust-version = "1.68" edition = "2021" license = "MIT" diff --git a/README.md b/README.md index b8598615..45153b31 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ $ set TELOXIDE_TOKEN= $ $env:TELOXIDE_TOKEN= ``` - 4. Make sure that your Rust compiler is up to date (`teloxide` currently requires rustc at least version 1.65): + 4. Make sure that your Rust compiler is up to date (`teloxide` currently requires rustc at least version 1.68): ```bash # If you're using stable $ rustup update stable diff --git a/crates/teloxide-core/CHANGELOG.md b/crates/teloxide-core/CHANGELOG.md index 001f12c0..085e5995 100644 --- a/crates/teloxide-core/CHANGELOG.md +++ b/crates/teloxide-core/CHANGELOG.md @@ -54,7 +54,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CallbackGame`, `ForumTopicClosed`, `ForumTopicReopened`, `GeneralForumTopicHidden`, `GeneralForumTopicUnhidden` and `WriteAccessAllowed` structures are now defined as named (`struct S {}`) instead of unit (`struct S;`) in order to fix their deserialization ([#876][pr876]) - `Download` now uses GAT feature on the `Fut` and `Err` associated types, instead of a lifetime on the whole trait ([#885][pr885]) -- MSRV (Minimal Supported Rust Version) was bumped from `1.64.0` to `1.65.0` - Renamed `ForumTopic::message_thread_id` into `thread_id` ([#887][pr887]) - `ForumTopic::thread_id` and `Message::thread_id` now use `ThreadId` instead of `i32` ([#887][pr887]) - `message_thread_id` method parameters now use `ThreadId` instead of `i32` ([#887][pr887]) @@ -67,12 +66,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `u32` for sizes and `Seconds` for timespans in `InlineQueryResult*` ([#887][pr887]) - `SendGame::reply_to_message_id`, `SendSticker::reply_to_message_id` and `SendInvoice::reply_to_message_id` now use `MessageId` instead of `i32` ([#887][pr887]) - Use `UpdateId` for `Update::id` ([#892][pr892]) +- MSRV (Minimal Supported Rust Version) was bumped from `1.64.0` to `1.68.0` ([#950][pr950]) [pr852]: https://github.com/teloxide/teloxide/pull/853 [pr859]: https://github.com/teloxide/teloxide/pull/859 [pr876]: https://github.com/teloxide/teloxide/pull/876 [pr885]: https://github.com/teloxide/teloxide/pull/885 [pr892]: https://github.com/teloxide/teloxide/pull/892 +[pr950]: https://github.com/teloxide/teloxide/pull/950 ### Deprecated diff --git a/crates/teloxide-core/README.md b/crates/teloxide-core/README.md index bee0e8f6..d059b7c1 100644 --- a/crates/teloxide-core/README.md +++ b/crates/teloxide-core/README.md @@ -27,7 +27,7 @@ ```toml teloxide-core = "0.9" ``` -_Compiler support: requires rustc 1.65+_. +_Compiler support: requires rustc 1.68+_. [`teloxide`]: https://docs.rs/teloxide [Telegram Bot API]: https://core.telegram.org/bots/api diff --git a/crates/teloxide-core/src/lib.rs b/crates/teloxide-core/src/lib.rs index 0a710774..679a4859 100644 --- a/crates/teloxide-core/src/lib.rs +++ b/crates/teloxide-core/src/lib.rs @@ -7,7 +7,7 @@ //!```toml //! teloxide-core = "0.9" //! ``` -//! _Compiler support: requires rustc 1.65+_. +//! _Compiler support: requires rustc 1.68+_. //! //! ``` //! # async { diff --git a/crates/teloxide-macros/CHANGELOG.md b/crates/teloxide-macros/CHANGELOG.md index 57b65163..e91ac50d 100644 --- a/crates/teloxide-macros/CHANGELOG.md +++ b/crates/teloxide-macros/CHANGELOG.md @@ -9,14 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Now you can use `#[command(command_separator="sep")]` (default is a whitespace character) to set the separator between command and its arguments ([issue #897](https://github.com/teloxide/teloxide/issues/897)) +- Now you can use `/// doc comment` for the command help message ([PR #861](https://github.com/teloxide/teloxide/pull/861)). +- Now you can use `#[command(hide)]` to hide a command from the help message ([PR #862](https://github.com/teloxide/teloxide/pull/862)) ### Fixed - Fix `split` parser for tuple variants with len < 2 ([issue #834](https://github.com/teloxide/teloxide/issues/834)) -### Added -- Now you can use `/// doc comment` for the command help message ([PR #861](https://github.com/teloxide/teloxide/pull/861)). -- Now you can use `#[command(hide)]` to hide a command from the help message ([PR #862](https://github.com/teloxide/teloxide/pull/862)) +### Changed + +- MSRV (Minimal Supported Rust Version) was bumped from `1.64.0` to `1.68.0` ([PR 950][https://github.com/teloxide/teloxide/pull/950]) ### Deprecated From 81b4c248a1bb04de261e648da9b6644fefbb3cfa Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 4 Oct 2023 22:01:56 +0400 Subject: [PATCH 23/26] Silence clippy --- crates/teloxide-core/src/adaptors/throttle/worker.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/teloxide-core/src/adaptors/throttle/worker.rs b/crates/teloxide-core/src/adaptors/throttle/worker.rs index 5e1bc116..26d08493 100644 --- a/crates/teloxide-core/src/adaptors/throttle/worker.rs +++ b/crates/teloxide-core/src/adaptors/throttle/worker.rs @@ -297,6 +297,8 @@ fn answer_info(rx: &mut mpsc::Receiver, limits: &mut Limits) { } } +// FIXME: https://github.com/rust-lang/rust-clippy/issues/11610 +#[allow(clippy::needless_pass_by_ref_mut)] async fn freeze( rx: &mut mpsc::Receiver, mut slow_mode: Option<&mut HashMap>, From 0c68c78626dc1a2e4c61be770d6e6503cf6349e7 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 5 Oct 2023 20:14:18 +0400 Subject: [PATCH 24/26] Make `ci-pass` job actually fail when some required job failed --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de4ee898..0d6d70fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: ci-pass: name: CI succeeded runs-on: ubuntu-latest + if: always() needs: - fmt @@ -44,7 +45,10 @@ jobs: - doc steps: - - run: exit 0 + - name: Check whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} fmt: name: fmt From 63636a578735c43b7b8594af16cc2f4f56389a88 Mon Sep 17 00:00:00 2001 From: mikhailantoshkin Date: Sun, 8 Oct 2023 11:05:17 +0900 Subject: [PATCH 25/26] Fix typos in documentation --- crates/teloxide-core/src/types/input_file.rs | 8 ++++---- crates/teloxide-core/src/types/input_media.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/teloxide-core/src/types/input_file.rs b/crates/teloxide-core/src/types/input_file.rs index 00948f8b..77bcf4bc 100644 --- a/crates/teloxide-core/src/types/input_file.rs +++ b/crates/teloxide-core/src/types/input_file.rs @@ -114,7 +114,7 @@ impl InputFile { } /// Shorthand for `Self { file_name: None, inner, id: default() }` - /// (private because `InnerFile` iы private implementation detail) + /// (private because `InnerFile` is private implementation detail) fn new(inner: InnerFile) -> Self { Self { file_name: None, inner, id: OnceCell::new() } } @@ -258,7 +258,7 @@ impl Read { let res = ArcBox::>::try_from(self.inner); match res { // Fast/easy path: this is the only file copy, so we can just forward the underlying - // `dyn AsynсRead` via some adaptors to reqwest. + // `dyn AsyncRead` via some adaptors to reqwest. Ok(arc_box) => { let fr = FramedRead::new(ExclusiveArcAsyncRead(arc_box), BytesDecoder); @@ -270,7 +270,7 @@ impl Read { } } - // Slow path: either wait until someone will read the whole `dyn AsynсRead` into + // Slow path: either wait until someone will read the whole `dyn AsyncRead` into // a buffer, or be the one who reads let body = self.into_shared_body().await; @@ -321,7 +321,7 @@ impl Read { let _ = self.notify.send(()); } - // Wait until `dyn AsynсRead` is read into a buffer, if it hasn't been read yet + // Wait until `dyn AsyncRead` is read into a buffer, if it hasn't been read yet None if self.buf.get().is_none() => { // Error indicates that the sender was dropped, by we hold `Arc`, so // this can't happen diff --git a/crates/teloxide-core/src/types/input_media.rs b/crates/teloxide-core/src/types/input_media.rs index 9b9effcc..aaefba5a 100644 --- a/crates/teloxide-core/src/types/input_media.rs +++ b/crates/teloxide-core/src/types/input_media.rs @@ -452,7 +452,7 @@ pub struct InputMediaDocument { /// using multipart/form-data. pub thumb: Option, - /// Caption of the document to be sent, 0-1024 charactersю + /// Caption of the document to be sent, 0-1024 characters. pub caption: Option, /// Send [Markdown] or [HTML], if you want Telegram apps to show [bold, From a6e53cf3b8a49904febc2f3083e51f2a90e8fd66 Mon Sep 17 00:00:00 2001 From: mikhailantoshkin Date: Sun, 8 Oct 2023 11:12:45 +0900 Subject: [PATCH 26/26] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dceb57f..4b395c54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `UserId` instead of `i64` for `user_id` in `html::user_mention` and `markdown::user_mention` ([PR 896](https://github.com/teloxide/teloxide/pull/896)) - Greatly improved the speed of graceful shutdown (`^C`) ([PR 938](https://github.com/teloxide/teloxide/pull/938)) +- Fix typos in docstrings ([PR 953](https://github.com/teloxide/teloxide/pull/953)) ### Changed