mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 06:25:10 +01:00
Merge rustfmt.toml
s
This commit is contained in:
parent
1a56c6fc77
commit
1d84b2b76a
67 changed files with 341 additions and 1107 deletions
|
@ -6,11 +6,8 @@ use teloxide_core::{adaptors::trace, prelude::*, types::ChatAction};
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
let chat_id = ChatId(
|
let chat_id =
|
||||||
std::env::var("CHAT_ID")
|
ChatId(std::env::var("CHAT_ID").expect("Expected CHAT_ID env var").parse::<i64>()?);
|
||||||
.expect("Expected CHAT_ID env var")
|
|
||||||
.parse::<i64>()?,
|
|
||||||
);
|
|
||||||
|
|
||||||
let trace_settings = match std::env::var("TRACE").as_deref() {
|
let trace_settings = match std::env::var("TRACE").as_deref() {
|
||||||
Ok("EVERYTHING_VERBOSE") => trace::Settings::TRACE_EVERYTHING_VERBOSE,
|
Ok("EVERYTHING_VERBOSE") => trace::Settings::TRACE_EVERYTHING_VERBOSE,
|
||||||
|
|
|
@ -7,19 +7,15 @@ use teloxide_core::{
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
let chat_id = ChatId(
|
let chat_id =
|
||||||
std::env::var("CHAT_ID")
|
ChatId(std::env::var("CHAT_ID").expect("Expected CHAT_ID env var").parse::<i64>()?);
|
||||||
.expect("Expected CHAT_ID env var")
|
|
||||||
.parse::<i64>()?,
|
|
||||||
);
|
|
||||||
|
|
||||||
let bot = Bot::from_env().parse_mode(ParseMode::MarkdownV2);
|
let bot = Bot::from_env().parse_mode(ParseMode::MarkdownV2);
|
||||||
|
|
||||||
let Me { user: me, .. } = bot.get_me().await?;
|
let Me { user: me, .. } = bot.get_me().await?;
|
||||||
|
|
||||||
bot.send_dice(chat_id).emoji(DiceEmoji::Dice).await?;
|
bot.send_dice(chat_id).emoji(DiceEmoji::Dice).await?;
|
||||||
bot.send_message(chat_id, format!("Hi, my name is **{}** 👋", me.first_name))
|
bot.send_message(chat_id, format!("Hi, my name is **{}** 👋", me.first_name)).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
format_code_in_doc_comments = true
|
|
||||||
wrap_comments = true
|
|
||||||
format_strings = true
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
use_field_init_shorthand = true
|
|
||||||
merge_derives = false
|
|
|
@ -32,10 +32,7 @@ impl<B> CacheMe<B> {
|
||||||
///
|
///
|
||||||
/// [`RequesterExt::cache_me`]: crate::requests::RequesterExt::cache_me
|
/// [`RequesterExt::cache_me`]: crate::requests::RequesterExt::cache_me
|
||||||
pub fn new(bot: B) -> CacheMe<B> {
|
pub fn new(bot: B) -> CacheMe<B> {
|
||||||
Self {
|
Self { bot, me: Arc::new(OnceCell::new()) }
|
||||||
bot,
|
|
||||||
me: Arc::new(OnceCell::new()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows to access inner bot
|
/// Allows to access inner bot
|
||||||
|
|
|
@ -24,9 +24,7 @@ impl<'a, E> ErasedRequester<'a, E> {
|
||||||
where
|
where
|
||||||
B: Requester<Err = E> + 'a,
|
B: Requester<Err = E> + 'a,
|
||||||
{
|
{
|
||||||
Self {
|
Self { inner: Arc::new(requester) }
|
||||||
inner: Arc::new(requester),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,9 +37,7 @@ impl<E> std::fmt::Debug for ErasedRequester<'_, E> {
|
||||||
// NB. hand-written impl to avoid `E: Clone` bound
|
// NB. hand-written impl to avoid `E: Clone` bound
|
||||||
impl<E> Clone for ErasedRequester<'_, E> {
|
impl<E> Clone for ErasedRequester<'_, E> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self { inner: Arc::clone(&self.inner) }
|
||||||
inner: Arc::clone(&self.inner),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +50,7 @@ pub struct ErasedRequest<'a, T, E> {
|
||||||
// `T: Payload` required b/c of <https://github.com/rust-lang/rust/issues/102185>
|
// `T: Payload` required b/c of <https://github.com/rust-lang/rust/issues/102185>
|
||||||
impl<'a, T: Payload, E> ErasedRequest<'a, T, E> {
|
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,7 @@ impl<B> DefaultParseMode<B> {
|
||||||
///
|
///
|
||||||
/// [`RequesterExt::parse_mode`]: crate::requests::RequesterExt::parse_mode
|
/// [`RequesterExt::parse_mode`]: crate::requests::RequesterExt::parse_mode
|
||||||
pub fn new(bot: B, parse_mode: ParseMode) -> Self {
|
pub fn new(bot: B, parse_mode: ParseMode) -> Self {
|
||||||
Self {
|
Self { bot, mode: parse_mode }
|
||||||
bot,
|
|
||||||
mode: parse_mode,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows to access the inner bot.
|
/// Allows to access the inner bot.
|
||||||
|
|
|
@ -88,10 +88,7 @@ impl<B> Throttle<B> {
|
||||||
B: Requester + Clone,
|
B: Requester + Clone,
|
||||||
B::Err: AsResponseParameters,
|
B::Err: AsResponseParameters,
|
||||||
{
|
{
|
||||||
let settings = Settings {
|
let settings = Settings { limits, ..<_>::default() };
|
||||||
limits,
|
|
||||||
..<_>::default()
|
|
||||||
};
|
|
||||||
Self::with_settings(bot, settings)
|
Self::with_settings(bot, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +105,7 @@ impl<B> Throttle<B> {
|
||||||
let (info_tx, info_rx) = mpsc::channel(2);
|
let (info_tx, info_rx) = mpsc::channel(2);
|
||||||
|
|
||||||
let worker = worker(settings, rx, info_rx, bot.clone());
|
let worker = worker(settings, rx, info_rx, bot.clone());
|
||||||
let this = Self {
|
let this = Self { bot, queue: tx, info_tx };
|
||||||
bot,
|
|
||||||
queue: tx,
|
|
||||||
info_tx,
|
|
||||||
};
|
|
||||||
|
|
||||||
(this, worker)
|
(this, worker)
|
||||||
}
|
}
|
||||||
|
@ -164,10 +157,7 @@ impl<B> Throttle<B> {
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
self.info_tx
|
self.info_tx.send(InfoMessage::GetLimits { response: tx }).await.expect(WORKER_DIED);
|
||||||
.send(InfoMessage::GetLimits { response: tx })
|
|
||||||
.await
|
|
||||||
.expect(WORKER_DIED);
|
|
||||||
|
|
||||||
rx.await.expect(WORKER_DIED)
|
rx.await.expect(WORKER_DIED)
|
||||||
}
|
}
|
||||||
|
@ -178,10 +168,7 @@ impl<B> Throttle<B> {
|
||||||
pub async fn set_limits(&self, new: Limits) {
|
pub async fn set_limits(&self, new: Limits) {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
self.info_tx
|
self.info_tx.send(InfoMessage::SetLimits { new, response: tx }).await.ok();
|
||||||
.send(InfoMessage::SetLimits { new, response: tx })
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
rx.await.ok();
|
rx.await.ok();
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,12 +90,7 @@ pub(super) struct FreezeUntil {
|
||||||
// the request that it can be now executed, increase counts, add record to the
|
// the request that it can be now executed, increase counts, add record to the
|
||||||
// history.
|
// history.
|
||||||
pub(super) async fn worker<B>(
|
pub(super) async fn worker<B>(
|
||||||
Settings {
|
Settings { mut limits, mut on_queue_full, retry, check_slow_mode }: Settings,
|
||||||
mut limits,
|
|
||||||
mut on_queue_full,
|
|
||||||
retry,
|
|
||||||
check_slow_mode,
|
|
||||||
}: Settings,
|
|
||||||
mut rx: mpsc::Receiver<(ChatIdHash, RequestLock)>,
|
mut rx: mpsc::Receiver<(ChatIdHash, RequestLock)>,
|
||||||
mut info_rx: mpsc::Receiver<InfoMessage>,
|
mut info_rx: mpsc::Receiver<InfoMessage>,
|
||||||
bot: B,
|
bot: B,
|
||||||
|
@ -117,9 +112,8 @@ pub(super) async fn worker<B>(
|
||||||
|
|
||||||
let mut rx_is_closed = false;
|
let mut rx_is_closed = false;
|
||||||
|
|
||||||
let mut last_queue_full = Instant::now()
|
let mut last_queue_full =
|
||||||
.checked_sub(QUEUE_FULL_DELAY)
|
Instant::now().checked_sub(QUEUE_FULL_DELAY).unwrap_or_else(Instant::now);
|
||||||
.unwrap_or_else(Instant::now);
|
|
||||||
|
|
||||||
let (freeze_tx, mut freeze_rx) = mpsc::channel::<FreezeUntil>(1);
|
let (freeze_tx, mut freeze_rx) = mpsc::channel::<FreezeUntil>(1);
|
||||||
|
|
||||||
|
@ -214,10 +208,7 @@ pub(super) async fn worker<B>(
|
||||||
|
|
||||||
// as truncates which is ok since in case of truncation it would always be >=
|
// as truncates which is ok since in case of truncation it would always be >=
|
||||||
// limits.overall_s
|
// limits.overall_s
|
||||||
let used = history
|
let used = history.iter().take_while(|(_, time)| time > &sec_back).count() as u32;
|
||||||
.iter()
|
|
||||||
.take_while(|(_, time)| time > &sec_back)
|
|
||||||
.count() as u32;
|
|
||||||
let mut allowed = limits.messages_per_sec_overall.saturating_sub(used);
|
let mut allowed = limits.messages_per_sec_overall.saturating_sub(used);
|
||||||
|
|
||||||
if allowed == 0 {
|
if allowed == 0 {
|
||||||
|
|
|
@ -249,11 +249,7 @@ where
|
||||||
{
|
{
|
||||||
if self.settings.contains(Settings::TRACE_RESPONSES_VERBOSE) {
|
if self.settings.contains(Settings::TRACE_RESPONSES_VERBOSE) {
|
||||||
|response| {
|
|response| {
|
||||||
log::trace!(
|
log::trace!("Got response from `{}` request: {:?}", R::Payload::NAME, response)
|
||||||
"Got response from `{}` request: {:?}",
|
|
||||||
R::Payload::NAME,
|
|
||||||
response
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else if self.settings.contains(Settings::TRACE_RESPONSES) {
|
} else if self.settings.contains(Settings::TRACE_RESPONSES) {
|
||||||
|_| log::trace!("Got response from `{}` request", R::Payload::NAME)
|
|_| log::trace!("Got response from `{}` request", R::Payload::NAME)
|
||||||
|
@ -294,19 +290,13 @@ where
|
||||||
fn send(self) -> Self::Send {
|
fn send(self) -> Self::Send {
|
||||||
self.trace_request();
|
self.trace_request();
|
||||||
|
|
||||||
Send {
|
Send { trace_fn: self.trace_response_fn(), inner: self.inner.send() }
|
||||||
trace_fn: self.trace_response_fn(),
|
|
||||||
inner: self.inner.send(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_ref(&self) -> Self::SendRef {
|
fn send_ref(&self) -> Self::SendRef {
|
||||||
self.trace_request();
|
self.trace_request();
|
||||||
|
|
||||||
Send {
|
Send { trace_fn: self.trace_response_fn(), inner: self.inner.send_ref() }
|
||||||
trace_fn: self.trace_response_fn(),
|
|
||||||
inner: self.inner.send_ref(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,9 +71,7 @@ impl Bot {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
let client = net::default_reqwest_settings()
|
let client = net::default_reqwest_settings().build().expect("Client creation failed");
|
||||||
.build()
|
|
||||||
.expect("Client creation failed");
|
|
||||||
|
|
||||||
Self::with_client(token, client)
|
Self::with_client(token, client)
|
||||||
}
|
}
|
||||||
|
@ -98,11 +96,7 @@ impl Bot {
|
||||||
.expect("Failed to parse default Telegram bot API url"),
|
.expect("Failed to parse default Telegram bot API url"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self { token, api_url, client }
|
||||||
token,
|
|
||||||
api_url,
|
|
||||||
client,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `Bot` with the `TELOXIDE_TOKEN` & `TELOXIDE_PROXY`
|
/// Creates a new `Bot` with the `TELOXIDE_TOKEN` & `TELOXIDE_PROXY`
|
||||||
|
|
|
@ -114,10 +114,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
C: Into<Recipient>,
|
C: Into<Recipient>,
|
||||||
{
|
{
|
||||||
Self::SendAnimation::new(
|
Self::SendAnimation::new(self.clone(), payloads::SendAnimation::new(chat_id, animation))
|
||||||
self.clone(),
|
|
||||||
payloads::SendAnimation::new(chat_id, animation),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendVoice = MultipartRequest<payloads::SendVoice>;
|
type SendVoice = MultipartRequest<payloads::SendVoice>;
|
||||||
|
@ -135,10 +132,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
C: Into<Recipient>,
|
C: Into<Recipient>,
|
||||||
{
|
{
|
||||||
Self::SendVideoNote::new(
|
Self::SendVideoNote::new(self.clone(), payloads::SendVideoNote::new(chat_id, video_note))
|
||||||
self.clone(),
|
|
||||||
payloads::SendVideoNote::new(chat_id, video_note),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendMediaGroup = MultipartRequest<payloads::SendMediaGroup>;
|
type SendMediaGroup = MultipartRequest<payloads::SendMediaGroup>;
|
||||||
|
@ -276,10 +270,7 @@ impl Requester for Bot {
|
||||||
Q: Into<String>,
|
Q: Into<String>,
|
||||||
O: IntoIterator<Item = String>,
|
O: IntoIterator<Item = String>,
|
||||||
{
|
{
|
||||||
Self::SendPoll::new(
|
Self::SendPoll::new(self.clone(), payloads::SendPoll::new(chat_id, question, options))
|
||||||
self.clone(),
|
|
||||||
payloads::SendPoll::new(chat_id, question, options),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendDice = JsonRequest<payloads::SendDice>;
|
type SendDice = JsonRequest<payloads::SendDice>;
|
||||||
|
@ -325,10 +316,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
C: Into<Recipient>,
|
C: Into<Recipient>,
|
||||||
{
|
{
|
||||||
Self::KickChatMember::new(
|
Self::KickChatMember::new(self.clone(), payloads::KickChatMember::new(chat_id, user_id))
|
||||||
self.clone(),
|
|
||||||
payloads::KickChatMember::new(chat_id, user_id),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BanChatMember = JsonRequest<payloads::BanChatMember>;
|
type BanChatMember = JsonRequest<payloads::BanChatMember>;
|
||||||
|
@ -346,10 +334,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
C: Into<Recipient>,
|
C: Into<Recipient>,
|
||||||
{
|
{
|
||||||
Self::UnbanChatMember::new(
|
Self::UnbanChatMember::new(self.clone(), payloads::UnbanChatMember::new(chat_id, user_id))
|
||||||
self.clone(),
|
|
||||||
payloads::UnbanChatMember::new(chat_id, user_id),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RestrictChatMember = JsonRequest<payloads::RestrictChatMember>;
|
type RestrictChatMember = JsonRequest<payloads::RestrictChatMember>;
|
||||||
|
@ -568,10 +553,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
C: Into<Recipient>,
|
C: Into<Recipient>,
|
||||||
{
|
{
|
||||||
Self::PinChatMessage::new(
|
Self::PinChatMessage::new(self.clone(), payloads::PinChatMessage::new(chat_id, message_id))
|
||||||
self.clone(),
|
|
||||||
payloads::PinChatMessage::new(chat_id, message_id),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnpinChatMessage = JsonRequest<payloads::UnpinChatMessage>;
|
type UnpinChatMessage = JsonRequest<payloads::UnpinChatMessage>;
|
||||||
|
@ -892,10 +874,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
C: Into<Recipient>,
|
C: Into<Recipient>,
|
||||||
{
|
{
|
||||||
Self::DeleteMessage::new(
|
Self::DeleteMessage::new(self.clone(), payloads::DeleteMessage::new(chat_id, message_id))
|
||||||
self.clone(),
|
|
||||||
payloads::DeleteMessage::new(chat_id, message_id),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SendSticker = MultipartRequest<payloads::SendSticker>;
|
type SendSticker = MultipartRequest<payloads::SendSticker>;
|
||||||
|
@ -1131,10 +1110,7 @@ impl Requester for Bot {
|
||||||
where
|
where
|
||||||
G: Into<String>,
|
G: Into<String>,
|
||||||
{
|
{
|
||||||
Self::SendGame::new(
|
Self::SendGame::new(self.clone(), payloads::SendGame::new(chat_id, game_short_name))
|
||||||
self.clone(),
|
|
||||||
payloads::SendGame::new(chat_id, game_short_name),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetGameScore = JsonRequest<payloads::SetGameScore>;
|
type SetGameScore = JsonRequest<payloads::SetGameScore>;
|
||||||
|
|
|
@ -26,9 +26,7 @@ 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-09-23";
|
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().unwrap_or_default();
|
||||||
.read()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if !version.contains("nightly") {
|
if !version.contains("nightly") {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -81,12 +79,7 @@ pub fn ensure_files_contents<'a>(
|
||||||
let mut err_count = 0;
|
let mut err_count = 0;
|
||||||
|
|
||||||
for (path, contents) in files_and_contents {
|
for (path, contents) in files_and_contents {
|
||||||
let mut file = fs::File::options()
|
let mut file = fs::File::options().read(true).write(true).create(true).open(path).unwrap();
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.open(path)
|
|
||||||
.unwrap();
|
|
||||||
let mut old_contents = String::with_capacity(contents.len());
|
let mut old_contents = String::with_capacity(contents.len());
|
||||||
file.read_to_string(&mut old_contents).unwrap();
|
file.read_to_string(&mut old_contents).unwrap();
|
||||||
|
|
||||||
|
@ -146,28 +139,19 @@ pub fn replace_block(path: &Path, title: &str, new: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_offset = match &*starts {
|
let start_offset = match &*starts {
|
||||||
[] => panic!(
|
[] => panic!("Coulnd't find start of block {title} in {p}", p = path.display()),
|
||||||
"Coulnd't find start of block {title} in {p}",
|
|
||||||
p = path.display()
|
|
||||||
),
|
|
||||||
[offset] => offset.end,
|
[offset] => offset.end,
|
||||||
[..] => panic!(),
|
[..] => panic!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let end_offset = match &*ends {
|
let end_offset = match &*ends {
|
||||||
[] => panic!(
|
[] => panic!("Coulnd't find end of block {title} in {p}", p = path.display()),
|
||||||
"Coulnd't find end of block {title} in {p}",
|
|
||||||
p = path.display()
|
|
||||||
),
|
|
||||||
[offset] => offset.start,
|
[offset] => offset.start,
|
||||||
[..] => panic!(),
|
[..] => panic!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if end_offset < start_offset {
|
if end_offset < start_offset {
|
||||||
panic!(
|
panic!("End of the {title} block is located before the start in {p}", p = path.display());
|
||||||
"End of the {title} block is located before the start in {p}",
|
|
||||||
p = path.display()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("{}{}{}", &file[..start_offset], new, &file[end_offset..])
|
format!("{}{}{}", &file[..start_offset], new, &file[end_offset..])
|
||||||
|
|
|
@ -6,11 +6,7 @@ pub fn patch_schema(mut schema: Schema) -> Schema {
|
||||||
}
|
}
|
||||||
|
|
||||||
schema.methods.iter_mut().for_each(|method| {
|
schema.methods.iter_mut().for_each(|method| {
|
||||||
method
|
method.params.iter_mut().map(|p| &mut p.name).for_each(escape_kw);
|
||||||
.params
|
|
||||||
.iter_mut()
|
|
||||||
.map(|p| &mut p.name)
|
|
||||||
.for_each(escape_kw);
|
|
||||||
|
|
||||||
DOC_PATCHES.iter().for_each(|(key, patch)| match key {
|
DOC_PATCHES.iter().for_each(|(key, patch)| match key {
|
||||||
Target::Method(m) => {
|
Target::Method(m) => {
|
||||||
|
@ -18,10 +14,7 @@ pub fn patch_schema(mut schema: Schema) -> Schema {
|
||||||
method.doc.patch(patch, *key);
|
method.doc.patch(patch, *key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Target::Field {
|
Target::Field { method_name: m, field_name: f } => {
|
||||||
method_name: m,
|
|
||||||
field_name: f,
|
|
||||||
} => {
|
|
||||||
if check(m, &method.names.0) {
|
if check(m, &method.names.0) {
|
||||||
method
|
method
|
||||||
.params
|
.params
|
||||||
|
@ -34,10 +27,7 @@ pub fn patch_schema(mut schema: Schema) -> Schema {
|
||||||
if check(m, &method.names.0) {
|
if check(m, &method.names.0) {
|
||||||
method.doc.patch(patch, *key);
|
method.doc.patch(patch, *key);
|
||||||
|
|
||||||
method
|
method.params.iter_mut().for_each(|p| p.descr.patch(patch, *key))
|
||||||
.params
|
|
||||||
.iter_mut()
|
|
||||||
.for_each(|p| p.descr.patch(patch, *key))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -164,24 +154,18 @@ static DOC_PATCHES: &[(Target, Patch)] = &[
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum Target<'a> {
|
enum Target<'a> {
|
||||||
Any {
|
Any { method_name: Option<&'a str> },
|
||||||
method_name: Option<&'a str>,
|
|
||||||
},
|
|
||||||
Method(Option<&'a str>),
|
Method(Option<&'a str>),
|
||||||
Field {
|
Field { method_name: Option<&'a str>, field_name: Option<&'a str> },
|
||||||
method_name: Option<&'a str>,
|
|
||||||
field_name: Option<&'a str>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Target<'a> {
|
impl<'a> Target<'a> {
|
||||||
fn is_exact(&self) -> bool {
|
fn is_exact(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Target::Method(m) => m.is_some(),
|
Target::Method(m) => m.is_some(),
|
||||||
Target::Field {
|
Target::Field { method_name, field_name } => {
|
||||||
method_name,
|
method_name.is_some() && field_name.is_some()
|
||||||
field_name,
|
}
|
||||||
} => method_name.is_some() && field_name.is_some(),
|
|
||||||
Target::Any { method_name: _ } => false,
|
Target::Any { method_name: _ } => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,8 +192,7 @@ impl Doc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Patch::AddLink { name, value } => {
|
Patch::AddLink { name, value } => {
|
||||||
self.md_links
|
self.md_links.insert((*name).to_owned(), (*value).to_owned());
|
||||||
.insert((*name).to_owned(), (*value).to_owned());
|
|
||||||
}
|
}
|
||||||
// Patch::RemoveLink { name } => drop(self.md_links.remove(*name)),
|
// Patch::RemoveLink { name } => drop(self.md_links.remove(*name)),
|
||||||
// Patch::FullReplace { text, with } => {
|
// Patch::FullReplace { text, with } => {
|
||||||
|
@ -253,9 +236,7 @@ fn intra_links(doc: &mut Doc) {
|
||||||
|
|
||||||
for repl in repls_t {
|
for repl in repls_t {
|
||||||
if let Some(value) = doc.md_links.remove(repl.as_str()) {
|
if let Some(value) = doc.md_links.remove(repl.as_str()) {
|
||||||
doc.md = doc
|
doc.md = doc.md.replace(format!("[{}]", repl).as_str(), &format!("[`{}`]", repl));
|
||||||
.md
|
|
||||||
.replace(format!("[{}]", repl).as_str(), &format!("[`{}`]", repl));
|
|
||||||
doc.md_links.insert(format!("`{}`", repl), value);
|
doc.md_links.insert(format!("`{}`", repl), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,9 +244,7 @@ fn intra_links(doc: &mut Doc) {
|
||||||
for repl in repls_m {
|
for repl in repls_m {
|
||||||
if let Some(value) = doc.md_links.remove(repl.as_str()) {
|
if let Some(value) = doc.md_links.remove(repl.as_str()) {
|
||||||
let repln = to_uppercase(&repl);
|
let repln = to_uppercase(&repl);
|
||||||
doc.md = doc
|
doc.md = doc.md.replace(format!("[{}]", repl).as_str(), &format!("[`{}`]", repln));
|
||||||
.md
|
|
||||||
.replace(format!("[{}]", repl).as_str(), &format!("[`{}`]", repln));
|
|
||||||
doc.md_links.insert(format!("`{}`", repln), value);
|
doc.md_links.insert(format!("`{}`", repln), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,21 +263,13 @@ fn to_uppercase(s: &str) -> String {
|
||||||
|
|
||||||
pub(crate) fn patch_ty(mut schema: Schema) -> Schema {
|
pub(crate) fn patch_ty(mut schema: Schema) -> Schema {
|
||||||
// URLs
|
// URLs
|
||||||
patch_types(
|
patch_types(&mut schema, Type::String, Type::Url, &[("set_webhook", "url")]);
|
||||||
&mut schema,
|
|
||||||
Type::String,
|
|
||||||
Type::Url,
|
|
||||||
&[("set_webhook", "url")],
|
|
||||||
);
|
|
||||||
|
|
||||||
patch_types(
|
patch_types(
|
||||||
&mut schema,
|
&mut schema,
|
||||||
Type::Option(Box::new(Type::String)),
|
Type::Option(Box::new(Type::String)),
|
||||||
Type::Option(Box::new(Type::Url)),
|
Type::Option(Box::new(Type::Url)),
|
||||||
&[
|
&[("answer_callback_query", "url"), ("send_invoice", "photo_url")],
|
||||||
("answer_callback_query", "url"),
|
|
||||||
("send_invoice", "photo_url"),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dates
|
// Dates
|
||||||
|
@ -317,10 +288,7 @@ pub(crate) fn patch_ty(mut schema: Schema) -> Schema {
|
||||||
&mut schema,
|
&mut schema,
|
||||||
Type::Option(Box::new(Type::i64)),
|
Type::Option(Box::new(Type::i64)),
|
||||||
Type::Option(Box::new(Type::DateTime)),
|
Type::Option(Box::new(Type::DateTime)),
|
||||||
&[
|
&[("create_chat_invite_link", "expire_date"), ("edit_chat_invite_link", "expire_date")],
|
||||||
("create_chat_invite_link", "expire_date"),
|
|
||||||
("edit_chat_invite_link", "expire_date"),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
schema
|
schema
|
||||||
|
|
|
@ -110,10 +110,9 @@ pub enum ApiError {
|
||||||
/// 1. [`EditMessageText`]
|
/// 1. [`EditMessageText`]
|
||||||
///
|
///
|
||||||
/// [`EditMessageText`]: crate::payloads::EditMessageText
|
/// [`EditMessageText`]: crate::payloads::EditMessageText
|
||||||
#[serde(
|
#[serde(rename = "Bad Request: message is not modified: specified new message content and \
|
||||||
rename = "Bad Request: message is not modified: specified new message content and reply \
|
reply markup are exactly the same as a current content and reply markup \
|
||||||
markup are exactly the same as a current content and reply markup of the message"
|
of the message")]
|
||||||
)]
|
|
||||||
#[error(
|
#[error(
|
||||||
"Bad Request: message is not modified: specified new message content and reply markup are \
|
"Bad Request: message is not modified: specified new message content and reply markup are \
|
||||||
exactly the same as a current content and reply markup of the message"
|
exactly the same as a current content and reply markup of the message"
|
||||||
|
@ -390,10 +389,8 @@ pub enum ApiError {
|
||||||
/// 1. [`AnswerCallbackQuery`]
|
/// 1. [`AnswerCallbackQuery`]
|
||||||
///
|
///
|
||||||
/// [`AnswerCallbackQuery`]: crate::payloads::AnswerCallbackQuery
|
/// [`AnswerCallbackQuery`]: crate::payloads::AnswerCallbackQuery
|
||||||
#[serde(
|
#[serde(rename = "Bad Request: query is too old and response timeout expired or query id is \
|
||||||
rename = "Bad Request: query is too old and response timeout expired or query id is \
|
invalid")]
|
||||||
invalid"
|
|
||||||
)]
|
|
||||||
#[error("Bad Request: query is too old and response timeout expired or query id is invalid")]
|
#[error("Bad Request: query is too old and response timeout expired or query id is invalid")]
|
||||||
InvalidQueryId,
|
InvalidQueryId,
|
||||||
|
|
||||||
|
@ -424,10 +421,8 @@ pub enum ApiError {
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::payloads::SendMessage
|
/// [`SendMessage`]: crate::payloads::SendMessage
|
||||||
#[serde(
|
#[serde(rename = "Bad Request: can't parse inline keyboard button: Text buttons are \
|
||||||
rename = "Bad Request: can't parse inline keyboard button: Text buttons are unallowed in \
|
unallowed in the inline keyboard")]
|
||||||
the inline keyboard"
|
|
||||||
)]
|
|
||||||
#[error(
|
#[error(
|
||||||
"Bad Request: can't parse inline keyboard button: Text buttons are unallowed in the \
|
"Bad Request: can't parse inline keyboard button: Text buttons are unallowed in the \
|
||||||
inline keyboard"
|
inline keyboard"
|
||||||
|
@ -614,10 +609,8 @@ pub enum ApiError {
|
||||||
/// 1. [`SetWebhook`]
|
/// 1. [`SetWebhook`]
|
||||||
///
|
///
|
||||||
/// [`SetWebhook`]: crate::payloads::SetWebhook
|
/// [`SetWebhook`]: crate::payloads::SetWebhook
|
||||||
#[serde(
|
#[serde(rename = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 \
|
||||||
rename = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or \
|
or 8443")]
|
||||||
8443"
|
|
||||||
)]
|
|
||||||
#[error("Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or 8443")]
|
#[error("Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or 8443")]
|
||||||
BadWebhookPort,
|
BadWebhookPort,
|
||||||
|
|
||||||
|
@ -627,9 +620,7 @@ pub enum ApiError {
|
||||||
/// 1. [`SetWebhook`]
|
/// 1. [`SetWebhook`]
|
||||||
///
|
///
|
||||||
/// [`SetWebhook`]: crate::payloads::SetWebhook
|
/// [`SetWebhook`]: crate::payloads::SetWebhook
|
||||||
#[serde(
|
#[serde(rename = "Bad Request: bad webhook: Failed to resolve host: Name or service not known")]
|
||||||
rename = "Bad Request: bad webhook: Failed to resolve host: Name or service not known"
|
|
||||||
)]
|
|
||||||
#[error("Bad Request: bad webhook: Failed to resolve host: Name or service not known")]
|
#[error("Bad Request: bad webhook: Failed to resolve host: Name or service not known")]
|
||||||
UnknownHost,
|
UnknownHost,
|
||||||
|
|
||||||
|
@ -732,10 +723,8 @@ pub enum ApiError {
|
||||||
/// 1. [`GetUpdates`]
|
/// 1. [`GetUpdates`]
|
||||||
///
|
///
|
||||||
/// [`GetUpdates`]: crate::payloads::GetUpdates
|
/// [`GetUpdates`]: crate::payloads::GetUpdates
|
||||||
#[serde(
|
#[serde(rename = "Conflict: terminated by other getUpdates request; make sure that only one \
|
||||||
rename = "Conflict: terminated by other getUpdates request; make sure that only one bot \
|
bot instance is running")]
|
||||||
instance is running"
|
|
||||||
)]
|
|
||||||
#[error(
|
#[error(
|
||||||
"Conflict: terminated by other getUpdates request; make sure that only one bot instance \
|
"Conflict: terminated by other getUpdates request; make sure that only one bot instance \
|
||||||
is running"
|
is running"
|
||||||
|
|
|
@ -22,11 +22,7 @@
|
||||||
//! let me = bot.get_me().await?;
|
//! let me = bot.get_me().await?;
|
||||||
//!
|
//!
|
||||||
//! bot.send_dice(chat_id).emoji(DiceEmoji::Dice).await?;
|
//! bot.send_dice(chat_id).emoji(DiceEmoji::Dice).await?;
|
||||||
//! bot.send_message(
|
//! bot.send_message(chat_id, format!("Hi, my name is **{}** 👋", me.user.first_name)).await?;
|
||||||
//! chat_id,
|
|
||||||
//! format!("Hi, my name is **{}** 👋", me.user.first_name),
|
|
||||||
//! )
|
|
||||||
//! .await?;
|
|
||||||
//! # Ok::<_, Box<dyn std::error::Error>>(()) };
|
//! # Ok::<_, Box<dyn std::error::Error>>(()) };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -1293,11 +1293,9 @@ fn codegen_requester_forward() {
|
||||||
.filter(|p| !matches!(p.ty, Type::Option(_)))
|
.filter(|p| !matches!(p.ty, Type::Option(_)))
|
||||||
.flat_map(|p| match convert_for(&p.ty) {
|
.flat_map(|p| match convert_for(&p.ty) {
|
||||||
Convert::Id(_) => None,
|
Convert::Id(_) => None,
|
||||||
Convert::Into(ty) => Some(format!(
|
Convert::Into(ty) => {
|
||||||
"{}: Into<{}>",
|
Some(format!("{}: Into<{}>", &to_uppercase(prefixes[&*p.name]), ty))
|
||||||
&to_uppercase(prefixes[&*p.name]),
|
}
|
||||||
ty
|
|
||||||
)),
|
|
||||||
Convert::Collect(ty) => Some(format!(
|
Convert::Collect(ty) => Some(format!(
|
||||||
"{}: IntoIterator<Item = {}>",
|
"{}: IntoIterator<Item = {}>",
|
||||||
&to_uppercase(prefixes[&*p.name]),
|
&to_uppercase(prefixes[&*p.name]),
|
||||||
|
@ -1306,11 +1304,8 @@ fn codegen_requester_forward() {
|
||||||
})
|
})
|
||||||
.join(",\n ");
|
.join(",\n ");
|
||||||
|
|
||||||
let generics = if generics.is_empty() {
|
let generics =
|
||||||
String::from("")
|
if generics.is_empty() { String::from("") } else { format!("<{}>", generics) };
|
||||||
} else {
|
|
||||||
format!("<{}>", generics)
|
|
||||||
};
|
|
||||||
|
|
||||||
let where_clause = if where_clause.is_empty() {
|
let where_clause = if where_clause.is_empty() {
|
||||||
String::from("")
|
String::from("")
|
||||||
|
|
|
@ -80,11 +80,7 @@ pub fn default_reqwest_settings() -> reqwest::ClientBuilder {
|
||||||
///
|
///
|
||||||
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
|
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
|
||||||
fn method_url(base: reqwest::Url, token: &str, method_name: &str) -> reqwest::Url {
|
fn method_url(base: reqwest::Url, token: &str, method_name: &str) -> reqwest::Url {
|
||||||
base.join(&format!(
|
base.join(&format!("/bot{token}/{method}", token = token, method = method_name))
|
||||||
"/bot{token}/{method}",
|
|
||||||
token = token,
|
|
||||||
method = method_name
|
|
||||||
))
|
|
||||||
.expect("failed to format url")
|
.expect("failed to format url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +88,7 @@ fn method_url(base: reqwest::Url, token: &str, method_name: &str) -> reqwest::Ur
|
||||||
///
|
///
|
||||||
/// [Telegram documentation]: https://core.telegram.org/bots/api#file
|
/// [Telegram documentation]: https://core.telegram.org/bots/api#file
|
||||||
fn file_url(base: reqwest::Url, token: &str, file_path: &str) -> reqwest::Url {
|
fn file_url(base: reqwest::Url, token: &str, file_path: &str) -> reqwest::Url {
|
||||||
base.join(&format!(
|
base.join(&format!("file/bot{token}/{file}", token = token, file = file_path))
|
||||||
"file/bot{token}/{file}",
|
|
||||||
token = token,
|
|
||||||
file = file_path
|
|
||||||
))
|
|
||||||
.expect("failed to format url")
|
.expect("failed to format url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,7 @@ pub fn download_file<'o, D>(
|
||||||
where
|
where
|
||||||
D: ?Sized + AsyncWrite + Unpin,
|
D: ?Sized + AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
client
|
client.get(file_url(api_url, token, path)).send().then(move |r| async move {
|
||||||
.get(file_url(api_url, token, path))
|
|
||||||
.send()
|
|
||||||
.then(move |r| async move {
|
|
||||||
let mut res = r?.error_for_status()?;
|
let mut res = r?.error_for_status()?;
|
||||||
|
|
||||||
while let Some(chunk) = res.chunk().await? {
|
while let Some(chunk) = res.chunk().await? {
|
||||||
|
@ -119,11 +116,8 @@ pub fn download_file_stream(
|
||||||
token: &str,
|
token: &str,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> impl Stream<Item = reqwest::Result<Bytes>> + 'static {
|
) -> impl Stream<Item = reqwest::Result<Bytes>> + 'static {
|
||||||
client
|
client.get(file_url(api_url, token, path)).send().into_stream().flat_map(|res| {
|
||||||
.get(file_url(api_url, token, path))
|
match res.and_then(Response::error_for_status) {
|
||||||
.send()
|
|
||||||
.into_stream()
|
|
||||||
.flat_map(|res| match res.and_then(Response::error_for_status) {
|
|
||||||
Ok(res) => Either::Left(unfold(res, |mut res| async {
|
Ok(res) => Either::Left(unfold(res, |mut res| async {
|
||||||
match res.chunk().await {
|
match res.chunk().await {
|
||||||
Err(err) => Some((Err(err), res)),
|
Err(err) => Some((Err(err), res)),
|
||||||
|
@ -132,5 +126,6 @@ pub fn download_file_stream(
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
Err(err) => Either::Right(once(ready(Err(err)))),
|
Err(err) => Either::Right(once(ready(Err(err)))),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,9 +100,6 @@ where
|
||||||
let text = response.text().await?;
|
let text = response.text().await?;
|
||||||
|
|
||||||
serde_json::from_str::<TelegramResponse<T>>(&text)
|
serde_json::from_str::<TelegramResponse<T>>(&text)
|
||||||
.map_err(|source| RequestError::InvalidJson {
|
.map_err(|source| RequestError::InvalidJson { source, raw: text.into() })?
|
||||||
source,
|
|
||||||
raw: text.into(),
|
|
||||||
})?
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,7 @@ impl<R> From<TelegramResponse<R>> for ResponseResult<R> {
|
||||||
fn from(this: TelegramResponse<R>) -> ResponseResult<R> {
|
fn from(this: TelegramResponse<R>) -> ResponseResult<R> {
|
||||||
match this {
|
match this {
|
||||||
TelegramResponse::Ok { response, .. } => Ok(response),
|
TelegramResponse::Ok { response, .. } => Ok(response),
|
||||||
TelegramResponse::Err {
|
TelegramResponse::Err { response_parameters: Some(params), .. } => Err(match params {
|
||||||
response_parameters: Some(params),
|
|
||||||
..
|
|
||||||
} => Err(match params {
|
|
||||||
ResponseParameters::RetryAfter(i) => RequestError::RetryAfter(i),
|
ResponseParameters::RetryAfter(i) => RequestError::RetryAfter(i),
|
||||||
ResponseParameters::MigrateToChatId(to) => RequestError::MigrateToChatId(to),
|
ResponseParameters::MigrateToChatId(to) => RequestError::MigrateToChatId(to),
|
||||||
}),
|
}),
|
||||||
|
@ -61,10 +58,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
val,
|
val,
|
||||||
TelegramResponse::Err {
|
TelegramResponse::Err { error: ApiError::TerminatedByOtherGetUpdates, .. }
|
||||||
error: ApiError::TerminatedByOtherGetUpdates,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -237,10 +237,7 @@ fn codegen_payload_mods_and_reexports() {
|
||||||
let schema = schema::get();
|
let schema = schema::get();
|
||||||
let mut block = String::new();
|
let mut block = String::new();
|
||||||
|
|
||||||
schema
|
schema.methods.iter().for_each(|m| block.push_str(&format!("mod {};\n", m.names.2)));
|
||||||
.methods
|
|
||||||
.iter()
|
|
||||||
.for_each(|m| block.push_str(&format!("mod {};\n", m.names.2)));
|
|
||||||
|
|
||||||
block.push('\n');
|
block.push('\n');
|
||||||
|
|
||||||
|
|
|
@ -24,19 +24,12 @@ fn codegen_payloads() {
|
||||||
let uses = uses(&method);
|
let uses = uses(&method);
|
||||||
|
|
||||||
let method_doc = render_doc(&method.doc, method.sibling.as_deref());
|
let method_doc = render_doc(&method.doc, method.sibling.as_deref());
|
||||||
let eq_hash_derive = eq_hash_suitable(&method)
|
let eq_hash_derive = eq_hash_suitable(&method).then(|| " Eq, Hash,").unwrap_or("");
|
||||||
.then(|| " Eq, Hash,")
|
|
||||||
.unwrap_or("");
|
|
||||||
let default_derive = default_needed(&method).then(|| " Default,").unwrap_or("");
|
let default_derive = default_needed(&method).then(|| " Default,").unwrap_or("");
|
||||||
|
|
||||||
let return_ty = method.return_ty.to_string();
|
let return_ty = method.return_ty.to_string();
|
||||||
|
|
||||||
let required = params(
|
let required = params(method.params.iter().filter(|p| !matches!(&p.ty, Type::Option(_))));
|
||||||
method
|
|
||||||
.params
|
|
||||||
.iter()
|
|
||||||
.filter(|p| !matches!(&p.ty, Type::Option(_))),
|
|
||||||
);
|
|
||||||
let required = match &*required {
|
let required = match &*required {
|
||||||
"" => "".to_owned(),
|
"" => "".to_owned(),
|
||||||
_ => format!(" required {{\n{required}\n }}"),
|
_ => format!(" required {{\n{required}\n }}"),
|
||||||
|
@ -155,10 +148,8 @@ fn render_doc(doc: &Doc, sibling: Option<&str>) -> String {
|
||||||
let links = match &doc.md_links {
|
let links = match &doc.md_links {
|
||||||
links if links.is_empty() => String::new(),
|
links if links.is_empty() => String::new(),
|
||||||
links => {
|
links => {
|
||||||
let l: String = links
|
let l: String =
|
||||||
.iter()
|
links.iter().map(|(name, link)| format!("\n /// [{name}]: {link}")).collect();
|
||||||
.map(|(name, link)| format!("\n /// [{name}]: {link}"))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
format!("\n ///{l}")
|
format!("\n ///{l}")
|
||||||
}
|
}
|
||||||
|
@ -173,13 +164,7 @@ fn render_doc(doc: &Doc, sibling: Option<&str>) -> String {
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
[
|
[" /// ", &doc.md.replace('\n', "\n /// "), &sibling_note, &links].concat()
|
||||||
" /// ",
|
|
||||||
&doc.md.replace('\n', "\n /// "),
|
|
||||||
&sibling_note,
|
|
||||||
&links,
|
|
||||||
]
|
|
||||||
.concat()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eq_hash_suitable(method: &Method) -> bool {
|
fn eq_hash_suitable(method: &Method) -> bool {
|
||||||
|
@ -208,10 +193,7 @@ fn eq_hash_suitable(method: &Method) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_needed(method: &Method) -> bool {
|
fn default_needed(method: &Method) -> bool {
|
||||||
method
|
method.params.iter().all(|p| matches!(p.ty, Type::Option(_)))
|
||||||
.params
|
|
||||||
.iter()
|
|
||||||
.all(|p| matches!(p.ty, Type::Option(_)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn params(params: impl Iterator<Item = impl Borrow<Param>>) -> String {
|
fn params(params: impl Iterator<Item = impl Borrow<Param>>) -> String {
|
||||||
|
@ -258,12 +240,8 @@ fn params(params: impl Iterator<Item = impl Borrow<Param>>) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multipart_input_file_fields(m: &Method) -> Option<Vec<&str>> {
|
fn multipart_input_file_fields(m: &Method) -> Option<Vec<&str>> {
|
||||||
let fields: Vec<_> = m
|
let fields: Vec<_> =
|
||||||
.params
|
m.params.iter().filter(|&p| ty_is_multiparty(&p.ty)).map(|p| &*p.name).collect();
|
||||||
.iter()
|
|
||||||
.filter(|&p| ty_is_multiparty(&p.ty))
|
|
||||||
.map(|p| &*p.name)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
None
|
None
|
||||||
|
|
|
@ -14,17 +14,11 @@ pub trait MultipartPayload: Payload {
|
||||||
|
|
||||||
impl MultipartPayload for payloads::SendMediaGroup {
|
impl MultipartPayload for payloads::SendMediaGroup {
|
||||||
fn copy_files(&self, into: &mut dyn FnMut(InputFile)) {
|
fn copy_files(&self, into: &mut dyn FnMut(InputFile)) {
|
||||||
self.media
|
self.media.iter().flat_map(InputMedia::files).for_each(|f| f.copy_into(into))
|
||||||
.iter()
|
|
||||||
.flat_map(InputMedia::files)
|
|
||||||
.for_each(|f| f.copy_into(into))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_files(&mut self, into: &mut dyn FnMut(InputFile)) {
|
fn move_files(&mut self, into: &mut dyn FnMut(InputFile)) {
|
||||||
self.media
|
self.media.iter_mut().flat_map(InputMedia::files_mut).for_each(|f| f.move_into(into))
|
||||||
.iter_mut()
|
|
||||||
.flat_map(InputMedia::files_mut)
|
|
||||||
.for_each(|f| f.move_into(into))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,9 +72,7 @@ use crate::{
|
||||||
/// bot.send_message(chat_id, "<b>Text</b>").await?;
|
/// bot.send_message(chat_id, "<b>Text</b>").await?;
|
||||||
///
|
///
|
||||||
/// // This will use `ParseMode::MarkdownV2`
|
/// // This will use `ParseMode::MarkdownV2`
|
||||||
/// bot.send_message(chat_id, "**Text**")
|
/// bot.send_message(chat_id, "**Text**").parse_mode(ParseMode::MarkdownV2).await?;
|
||||||
/// .parse_mode(ParseMode::MarkdownV2)
|
|
||||||
/// .await?;
|
|
||||||
/// # Ok::<_, teloxide_core::RequestError>(())
|
/// # Ok::<_, teloxide_core::RequestError>(())
|
||||||
/// # };
|
/// # };
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -1284,11 +1282,9 @@ fn codegen_requester_methods() {
|
||||||
.filter(|p| !matches!(p.ty, Type::Option(_)))
|
.filter(|p| !matches!(p.ty, Type::Option(_)))
|
||||||
.flat_map(|p| match convert_for(&p.ty) {
|
.flat_map(|p| match convert_for(&p.ty) {
|
||||||
Convert::Id(_) => None,
|
Convert::Id(_) => None,
|
||||||
Convert::Into(ty) => Some(format!(
|
Convert::Into(ty) => {
|
||||||
"{}: Into<{}>",
|
Some(format!("{}: Into<{}>", &to_uppercase(prefixes[&*p.name]), ty))
|
||||||
&to_uppercase(prefixes[&*p.name]),
|
}
|
||||||
ty
|
|
||||||
)),
|
|
||||||
Convert::Collect(ty) => Some(format!(
|
Convert::Collect(ty) => Some(format!(
|
||||||
"{}: IntoIterator<Item = {}>",
|
"{}: IntoIterator<Item = {}>",
|
||||||
&to_uppercase(prefixes[&*p.name]),
|
&to_uppercase(prefixes[&*p.name]),
|
||||||
|
@ -1297,11 +1293,8 @@ fn codegen_requester_methods() {
|
||||||
})
|
})
|
||||||
.join(",\n ");
|
.join(",\n ");
|
||||||
|
|
||||||
let generics = if generics.is_empty() {
|
let generics =
|
||||||
String::from("")
|
if generics.is_empty() { String::from("") } else { format!("<{}>", generics) };
|
||||||
} else {
|
|
||||||
format!("<{}>", generics)
|
|
||||||
};
|
|
||||||
|
|
||||||
let where_clause = if where_clause.is_empty() {
|
let where_clause = if where_clause.is_empty() {
|
||||||
String::from("")
|
String::from("")
|
||||||
|
|
|
@ -99,11 +99,7 @@ mod tests {
|
||||||
async fn issue_473() {
|
async fn issue_473() {
|
||||||
to_form_ref(
|
to_form_ref(
|
||||||
&payloads::SendPhoto::new(ChatId(0), InputFile::file_id("0")).caption_entities([
|
&payloads::SendPhoto::new(ChatId(0), InputFile::file_id("0")).caption_entities([
|
||||||
MessageEntity {
|
MessageEntity { kind: MessageEntityKind::Url, offset: 0, length: 0 },
|
||||||
kind: MessageEntityKind::Url,
|
|
||||||
offset: 0,
|
|
||||||
length: 0,
|
|
||||||
},
|
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -131,9 +127,7 @@ mod tests {
|
||||||
File::open("../../media/example.gif").await.unwrap(),
|
File::open("../../media/example.gif").await.unwrap(),
|
||||||
))
|
))
|
||||||
.thumb(InputFile::read(
|
.thumb(InputFile::read(
|
||||||
File::open("../../media/teloxide-core-logo.png")
|
File::open("../../media/teloxide-core-logo.png").await.unwrap(),
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
))
|
))
|
||||||
.duration(17),
|
.duration(17),
|
||||||
),
|
),
|
||||||
|
@ -170,11 +164,7 @@ mod tests {
|
||||||
InputFile::file("../../media/teloxide-core-logo.png"),
|
InputFile::file("../../media/teloxide-core-logo.png"),
|
||||||
)
|
)
|
||||||
.caption_entities(entities())
|
.caption_entities(entities())
|
||||||
.thumb(InputFile::read(
|
.thumb(InputFile::read(File::open("../../media/teloxide-core-logo.png").await.unwrap()))
|
||||||
File::open("../../media/teloxide-core-logo.png")
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
))
|
|
||||||
.allow_sending_without_reply(true),
|
.allow_sending_without_reply(true),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -185,18 +175,10 @@ mod tests {
|
||||||
<_>::into_iter([
|
<_>::into_iter([
|
||||||
MessageEntity::new(MessageEntityKind::Url, 0, 0),
|
MessageEntity::new(MessageEntityKind::Url, 0, 0),
|
||||||
MessageEntity::new(MessageEntityKind::Pre { language: None }, 0, 0),
|
MessageEntity::new(MessageEntityKind::Pre { language: None }, 0, 0),
|
||||||
MessageEntity::new(
|
MessageEntity::new(MessageEntityKind::Pre { language: Some(String::new()) }, 0, 0),
|
||||||
MessageEntityKind::Pre {
|
|
||||||
language: Some(String::new()),
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
MessageEntity::new(MessageEntityKind::Url, 0, 0),
|
MessageEntity::new(MessageEntityKind::Url, 0, 0),
|
||||||
MessageEntity::new(
|
MessageEntity::new(
|
||||||
MessageEntityKind::TextLink {
|
MessageEntityKind::TextLink { url: "https://example.com".parse().unwrap() },
|
||||||
url: "https://example.com".parse().unwrap(),
|
|
||||||
},
|
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
|
|
@ -62,10 +62,7 @@ impl Serializer for MultipartSerializer {
|
||||||
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
|
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
|
||||||
|
|
||||||
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||||
Ok(MultipartMapSerializer {
|
Ok(MultipartMapSerializer { form: Form::new(), key: None })
|
||||||
form: Form::new(),
|
|
||||||
key: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_struct(
|
fn serialize_struct(
|
||||||
|
@ -266,10 +263,7 @@ impl SerializeMap for MultipartMapSerializer {
|
||||||
where
|
where
|
||||||
T: Serialize,
|
T: Serialize,
|
||||||
{
|
{
|
||||||
let key = self
|
let key = self.key.take().expect("Value serialized before key or key is not string");
|
||||||
.key
|
|
||||||
.take()
|
|
||||||
.expect("Value serialized before key or key is not string");
|
|
||||||
|
|
||||||
let part = value.serialize(PartSerializer {})?;
|
let part = value.serialize(PartSerializer {})?;
|
||||||
|
|
||||||
|
@ -373,17 +367,11 @@ impl Serializer for PartSerializer {
|
||||||
_: &'static str,
|
_: &'static str,
|
||||||
_: usize,
|
_: usize,
|
||||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||||
Ok(JsonPartSerializer {
|
Ok(JsonPartSerializer { buf: String::new(), state: PartSerializerStructState::Empty })
|
||||||
buf: String::new(),
|
|
||||||
state: PartSerializerStructState::Empty,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||||
Ok(JsonPartSerializer {
|
Ok(JsonPartSerializer { buf: String::new(), state: PartSerializerStructState::Empty })
|
||||||
buf: String::new(),
|
|
||||||
state: PartSerializerStructState::Empty,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unimplemented
|
// Unimplemented
|
||||||
|
|
|
@ -303,10 +303,7 @@ pub(crate) mod serde_date_from_unix_timestamp {
|
||||||
{
|
{
|
||||||
let timestamp = i64::deserialize(deserializer)?;
|
let timestamp = i64::deserialize(deserializer)?;
|
||||||
|
|
||||||
Ok(DateTime::from_utc(
|
Ok(DateTime::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc))
|
||||||
NaiveDateTime::from_timestamp(timestamp, 0),
|
|
||||||
Utc,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,10 +345,7 @@ pub(crate) mod option_url_from_string {
|
||||||
|
|
||||||
let json = r#"{"url":"https://github.com/token"}"#;
|
let json = r#"{"url":"https://github.com/token"}"#;
|
||||||
let url: Struct = serde_json::from_str(json).unwrap();
|
let url: Struct = serde_json::from_str(json).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(url.url, Some(Url::from_str("https://github.com/token").unwrap()));
|
||||||
url.url,
|
|
||||||
Some(Url::from_str("https://github.com/token").unwrap())
|
|
||||||
);
|
|
||||||
assert_eq!(serde_json::to_string(&url).unwrap(), json.to_owned());
|
assert_eq!(serde_json::to_string(&url).unwrap(), json.to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,20 +59,12 @@ mod tests {
|
||||||
"mime_type":"video/gif",
|
"mime_type":"video/gif",
|
||||||
"file_size":6500}"#;
|
"file_size":6500}"#;
|
||||||
let expected = Animation {
|
let expected = Animation {
|
||||||
file: FileMeta {
|
file: FileMeta { id: "id".to_string(), unique_id: "".to_string(), size: 6500 },
|
||||||
id: "id".to_string(),
|
|
||||||
unique_id: "".to_string(),
|
|
||||||
size: 6500,
|
|
||||||
},
|
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
duration: 59,
|
duration: 59,
|
||||||
thumb: Some(PhotoSize {
|
thumb: Some(PhotoSize {
|
||||||
file: FileMeta {
|
file: FileMeta { id: "id".to_owned(), unique_id: "".to_owned(), size: 3452 },
|
||||||
id: "id".to_owned(),
|
|
||||||
unique_id: "".to_owned(),
|
|
||||||
size: 3452,
|
|
||||||
},
|
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -59,21 +59,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
let expected = Audio {
|
let expected = Audio {
|
||||||
file: FileMeta {
|
file: FileMeta { id: "id".to_string(), unique_id: "".to_string(), size: 123_456 },
|
||||||
id: "id".to_string(),
|
|
||||||
unique_id: "".to_string(),
|
|
||||||
size: 123_456,
|
|
||||||
},
|
|
||||||
duration: 60,
|
duration: 60,
|
||||||
performer: Some("Performer".to_string()),
|
performer: Some("Performer".to_string()),
|
||||||
title: Some("Title".to_string()),
|
title: Some("Title".to_string()),
|
||||||
mime_type: Some("application/zip".parse().unwrap()),
|
mime_type: Some("application/zip".parse().unwrap()),
|
||||||
thumb: Some(PhotoSize {
|
thumb: Some(PhotoSize {
|
||||||
file: FileMeta {
|
file: FileMeta { id: "id".to_owned(), unique_id: "".to_owned(), size: 3452 },
|
||||||
id: "id".to_owned(),
|
|
||||||
unique_id: "".to_owned(),
|
|
||||||
size: 3452,
|
|
||||||
},
|
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -20,10 +20,7 @@ impl BotCommand {
|
||||||
S1: Into<String>,
|
S1: Into<String>,
|
||||||
S2: Into<String>,
|
S2: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { command: command.into(), description: description.into() }
|
||||||
command: command.into(),
|
|
||||||
description: description.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command<S>(mut self, val: S) -> Self
|
pub fn command<S>(mut self, val: S) -> Self
|
||||||
|
|
|
@ -58,10 +58,7 @@ pub enum BotCommandScope {
|
||||||
fn issue_486() {
|
fn issue_486() {
|
||||||
use crate::types::ChatId;
|
use crate::types::ChatId;
|
||||||
|
|
||||||
serde_json::to_string(&BotCommandScope::Chat {
|
serde_json::to_string(&BotCommandScope::Chat { chat_id: Recipient::Id(ChatId(0)) }).unwrap();
|
||||||
chat_id: Recipient::Id(ChatId(0)),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
serde_json::to_string(&BotCommandScope::ChatAdministrators {
|
serde_json::to_string(&BotCommandScope::ChatAdministrators {
|
||||||
chat_id: Recipient::Id(ChatId(0)),
|
chat_id: Recipient::Id(ChatId(0)),
|
||||||
|
|
|
@ -205,35 +205,20 @@ impl Chat {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_group(&self) -> bool {
|
pub fn is_group(&self) -> bool {
|
||||||
matches!(
|
matches!(self.kind, ChatKind::Public(ChatPublic { kind: PublicChatKind::Group(_), .. }))
|
||||||
self.kind,
|
|
||||||
ChatKind::Public(ChatPublic {
|
|
||||||
kind: PublicChatKind::Group(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_supergroup(&self) -> bool {
|
pub fn is_supergroup(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.kind,
|
self.kind,
|
||||||
ChatKind::Public(ChatPublic {
|
ChatKind::Public(ChatPublic { kind: PublicChatKind::Supergroup(_), .. })
|
||||||
kind: PublicChatKind::Supergroup(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_channel(&self) -> bool {
|
pub fn is_channel(&self) -> bool {
|
||||||
matches!(
|
matches!(self.kind, ChatKind::Public(ChatPublic { kind: PublicChatKind::Channel(_), .. }))
|
||||||
self.kind,
|
|
||||||
ChatKind::Public(ChatPublic {
|
|
||||||
kind: PublicChatKind::Channel(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -104,9 +104,7 @@ mod tests {
|
||||||
/// Test that `ChatId` is serialized as the underlying integer
|
/// Test that `ChatId` is serialized as the underlying integer
|
||||||
#[test]
|
#[test]
|
||||||
fn deser() {
|
fn deser() {
|
||||||
let chat_id = S {
|
let chat_id = S { chat_id: ChatId(0xAA) };
|
||||||
chat_id: ChatId(0xAA),
|
|
||||||
};
|
|
||||||
let json = r#"{"chat_id":170}"#;
|
let json = r#"{"chat_id":170}"#;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -120,10 +118,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chonky_user_id_to_bare() {
|
fn chonky_user_id_to_bare() {
|
||||||
assert!(matches!(
|
assert!(matches!(ChatId(5298363099).to_bare(), BareChatId::User(UserId(5298363099))));
|
||||||
ChatId(5298363099).to_bare(),
|
|
||||||
BareChatId::User(UserId(5298363099))
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -137,19 +132,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Somewhat random numbers
|
// Somewhat random numbers
|
||||||
let ids = [
|
let ids =
|
||||||
1,
|
[1, 4, 17, 34, 51, 777000, 1000000, 617136926, 1666111087, 1 << 20, (1 << 35) | 123456];
|
||||||
4,
|
|
||||||
17,
|
|
||||||
34,
|
|
||||||
51,
|
|
||||||
777000,
|
|
||||||
1000000,
|
|
||||||
617136926,
|
|
||||||
1666111087,
|
|
||||||
1 << 20,
|
|
||||||
(1 << 35) | 123456,
|
|
||||||
];
|
|
||||||
|
|
||||||
// rust 2021 when :(
|
// rust 2021 when :(
|
||||||
ids.iter().copied().for_each(assert_identity);
|
ids.iter().copied().for_each(assert_identity);
|
||||||
|
|
|
@ -324,9 +324,7 @@ impl ChatMemberKind {
|
||||||
pub fn can_manage_chat(&self) -> bool {
|
pub fn can_manage_chat(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_manage_chat, .. }) => *can_manage_chat,
|
||||||
can_manage_chat, ..
|
|
||||||
}) => *can_manage_chat,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => true,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,9 +343,7 @@ impl ChatMemberKind {
|
||||||
pub fn can_change_info(&self) -> bool {
|
pub fn can_change_info(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_change_info, .. }) => *can_change_info,
|
||||||
can_change_info, ..
|
|
||||||
}) => *can_change_info,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,9 +361,9 @@ impl ChatMemberKind {
|
||||||
pub fn can_post_messages(&self) -> bool {
|
pub fn can_post_messages(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_post_messages, .. }) => {
|
||||||
can_post_messages, ..
|
can_post_messages.unwrap_or_default()
|
||||||
}) => can_post_messages.unwrap_or_default(),
|
}
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -386,9 +382,9 @@ impl ChatMemberKind {
|
||||||
pub fn can_edit_messages(&self) -> bool {
|
pub fn can_edit_messages(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_edit_messages, .. }) => {
|
||||||
can_edit_messages, ..
|
can_edit_messages.unwrap_or_default()
|
||||||
}) => can_edit_messages.unwrap_or_default(),
|
}
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,10 +402,7 @@ impl ChatMemberKind {
|
||||||
pub fn can_delete_messages(&self) -> bool {
|
pub fn can_delete_messages(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_delete_messages, .. }) => *can_delete_messages,
|
||||||
can_delete_messages,
|
|
||||||
..
|
|
||||||
}) => *can_delete_messages,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,10 +420,9 @@ impl ChatMemberKind {
|
||||||
pub fn can_manage_video_chats(&self) -> bool {
|
pub fn can_manage_video_chats(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_manage_video_chats, .. }) => {
|
||||||
can_manage_video_chats,
|
*can_manage_video_chats
|
||||||
..
|
}
|
||||||
}) => *can_manage_video_chats,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,9 +446,7 @@ impl ChatMemberKind {
|
||||||
pub fn can_invite_users(&self) -> bool {
|
pub fn can_invite_users(&self) -> bool {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_invite_users, .. }) => *can_invite_users,
|
||||||
can_invite_users, ..
|
|
||||||
}) => *can_invite_users,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,10 +464,9 @@ impl ChatMemberKind {
|
||||||
pub fn can_restrict_members(&self) -> bool {
|
pub fn can_restrict_members(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_restrict_members, .. }) => {
|
||||||
can_restrict_members,
|
*can_restrict_members
|
||||||
..
|
}
|
||||||
}) => *can_restrict_members,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,9 +484,9 @@ impl ChatMemberKind {
|
||||||
pub fn can_pin_messages(&self) -> bool {
|
pub fn can_pin_messages(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_pin_messages, .. }) => {
|
||||||
can_pin_messages, ..
|
can_pin_messages.unwrap_or_default()
|
||||||
}) => can_pin_messages.unwrap_or_default(),
|
}
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -518,10 +507,7 @@ impl ChatMemberKind {
|
||||||
pub fn can_promote_members(&self) -> bool {
|
pub fn can_promote_members(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Owner(_) => true,
|
Self::Owner(_) => true,
|
||||||
Self::Administrator(Administrator {
|
Self::Administrator(Administrator { can_promote_members, .. }) => *can_promote_members,
|
||||||
can_promote_members,
|
|
||||||
..
|
|
||||||
}) => *can_promote_members,
|
|
||||||
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
Self::Member | Self::Restricted(_) | Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,9 +527,7 @@ impl ChatMemberKind {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn can_send_messages(&self) -> bool {
|
pub fn can_send_messages(&self) -> bool {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Restricted(Restricted {
|
Self::Restricted(Restricted { can_send_messages, .. }) => *can_send_messages,
|
||||||
can_send_messages, ..
|
|
||||||
}) => *can_send_messages,
|
|
||||||
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
||||||
Self::Left | Self::Banned(_) => false,
|
Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
|
@ -561,10 +545,9 @@ impl ChatMemberKind {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn can_send_media_messages(&self) -> bool {
|
pub fn can_send_media_messages(&self) -> bool {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Restricted(Restricted {
|
Self::Restricted(Restricted { can_send_media_messages, .. }) => {
|
||||||
can_send_media_messages,
|
*can_send_media_messages
|
||||||
..
|
}
|
||||||
}) => *can_send_media_messages,
|
|
||||||
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
||||||
Self::Left | Self::Banned(_) => false,
|
Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
|
@ -582,10 +565,9 @@ impl ChatMemberKind {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn can_send_other_messages(&self) -> bool {
|
pub fn can_send_other_messages(&self) -> bool {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Restricted(Restricted {
|
Self::Restricted(Restricted { can_send_other_messages, .. }) => {
|
||||||
can_send_other_messages,
|
*can_send_other_messages
|
||||||
..
|
}
|
||||||
}) => *can_send_other_messages,
|
|
||||||
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
||||||
Self::Left | Self::Banned(_) => false,
|
Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
|
@ -603,10 +585,9 @@ impl ChatMemberKind {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn can_add_web_page_previews(&self) -> bool {
|
pub fn can_add_web_page_previews(&self) -> bool {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Restricted(Restricted {
|
Self::Restricted(Restricted { can_add_web_page_previews, .. }) => {
|
||||||
can_add_web_page_previews,
|
*can_add_web_page_previews
|
||||||
..
|
}
|
||||||
}) => *can_add_web_page_previews,
|
|
||||||
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
Self::Owner(_) | Self::Administrator(_) | Self::Member => true,
|
||||||
Self::Left | Self::Banned(_) => false,
|
Self::Left | Self::Banned(_) => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,11 +114,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file,
|
file,
|
||||||
FileMeta {
|
FileMeta { id: "FILE_ID".to_owned(), unique_id: "FILE_UNIQUE_ID".to_owned(), size: 42 }
|
||||||
id: "FILE_ID".to_owned(),
|
|
||||||
unique_id: "FILE_UNIQUE_ID".to_owned(),
|
|
||||||
size: 42,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,7 @@ pub struct ForceReply {
|
||||||
impl ForceReply {
|
impl ForceReply {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self { force_reply: True, input_field_placeholder: None, selective: None }
|
||||||
force_reply: True,
|
|
||||||
input_field_placeholder: None,
|
|
||||||
selective: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_field_placeholder<T>(mut self, val: T) -> Self
|
pub fn input_field_placeholder<T>(mut self, val: T) -> Self
|
||||||
|
|
|
@ -96,10 +96,7 @@ impl InlineKeyboardButton {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { text: text.into(), kind }
|
||||||
text: text.into(),
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor for `InlineKeyboardButton` with [`Url`] kind.
|
/// Constructor for `InlineKeyboardButton` with [`Url`] kind.
|
||||||
|
@ -130,10 +127,7 @@ impl InlineKeyboardButton {
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
C: Into<String>,
|
C: Into<String>,
|
||||||
{
|
{
|
||||||
Self::new(
|
Self::new(text, InlineKeyboardButtonKind::CallbackData(callback_data.into()))
|
||||||
text,
|
|
||||||
InlineKeyboardButtonKind::CallbackData(callback_data.into()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor for `InlineKeyboardButton` with [`WebApp`] kind.
|
/// Constructor for `InlineKeyboardButton` with [`WebApp`] kind.
|
||||||
|
@ -154,10 +148,7 @@ impl InlineKeyboardButton {
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
Q: Into<String>,
|
Q: Into<String>,
|
||||||
{
|
{
|
||||||
Self::new(
|
Self::new(text, InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query.into()))
|
||||||
text,
|
|
||||||
InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query.into()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor for `InlineKeyboardButton` with
|
/// Constructor for `InlineKeyboardButton` with
|
||||||
|
|
|
@ -50,11 +50,7 @@ impl InlineKeyboardMarkup {
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: IntoIterator<Item = InlineKeyboardButton>,
|
I::Item: IntoIterator<Item = InlineKeyboardButton>,
|
||||||
{
|
{
|
||||||
self.inline_keyboard = val
|
self.inline_keyboard = val.into_iter().map(<_>::into_iter).map(<_>::collect).collect();
|
||||||
.into_iter()
|
|
||||||
.map(<_>::into_iter)
|
|
||||||
.map(<_>::collect)
|
|
||||||
.collect();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,9 +88,7 @@ mod tests {
|
||||||
let markup =
|
let markup =
|
||||||
InlineKeyboardMarkup::default().append_row(vec![button1.clone(), button2.clone()]);
|
InlineKeyboardMarkup::default().append_row(vec![button1.clone(), button2.clone()]);
|
||||||
|
|
||||||
let expected = InlineKeyboardMarkup {
|
let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1, button2]] };
|
||||||
inline_keyboard: vec![vec![button1, button2]],
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(markup, expected);
|
assert_eq!(markup, expected);
|
||||||
}
|
}
|
||||||
|
@ -108,9 +102,7 @@ mod tests {
|
||||||
.append_row(vec![button1.clone()])
|
.append_row(vec![button1.clone()])
|
||||||
.append_to_row(0, button2.clone());
|
.append_to_row(0, button2.clone());
|
||||||
|
|
||||||
let expected = InlineKeyboardMarkup {
|
let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1, button2]] };
|
||||||
inline_keyboard: vec![vec![button1, button2]],
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(markup, expected);
|
assert_eq!(markup, expected);
|
||||||
}
|
}
|
||||||
|
@ -124,9 +116,7 @@ mod tests {
|
||||||
.append_row(vec![button1.clone()])
|
.append_row(vec![button1.clone()])
|
||||||
.append_to_row(1, button2.clone());
|
.append_to_row(1, button2.clone());
|
||||||
|
|
||||||
let expected = InlineKeyboardMarkup {
|
let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1], vec![button2]] };
|
||||||
inline_keyboard: vec![vec![button1], vec![button2]],
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(markup, expected);
|
assert_eq!(markup, expected);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,11 +28,7 @@ impl InlineQueryResultGame {
|
||||||
S1: Into<String>,
|
S1: Into<String>,
|
||||||
S2: Into<String>,
|
S2: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { id: id.into(), game_short_name: game_short_name.into(), reply_markup: None }
|
||||||
id: id.into(),
|
|
||||||
game_short_name: game_short_name.into(),
|
|
||||||
reply_markup: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id<S>(mut self, val: S) -> Self
|
pub fn id<S>(mut self, val: S) -> Self
|
||||||
|
|
|
@ -116,11 +116,7 @@ impl InputFile {
|
||||||
/// Shorthand for `Self { file_name: None, inner, id: default() }`
|
/// Shorthand for `Self { file_name: None, inner, id: default() }`
|
||||||
/// (private because `InnerFile` iы private implementation detail)
|
/// (private because `InnerFile` iы private implementation detail)
|
||||||
fn new(inner: InnerFile) -> Self {
|
fn new(inner: InnerFile) -> Self {
|
||||||
Self {
|
Self { file_name: None, inner, id: OnceCell::new() }
|
||||||
file_name: None,
|
|
||||||
inner,
|
|
||||||
id: OnceCell::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns id of this file.
|
/// Returns id of this file.
|
||||||
|
@ -128,8 +124,7 @@ impl InputFile {
|
||||||
/// This is used to coordinate with `attach://`.
|
/// This is used to coordinate with `attach://`.
|
||||||
pub(crate) fn id(&self) -> &str {
|
pub(crate) fn id(&self) -> &str {
|
||||||
// FIXME: remove extra alloc
|
// FIXME: remove extra alloc
|
||||||
self.id
|
self.id.get_or_init(|| uuid::Uuid::new_v4().to_string().into())
|
||||||
.get_or_init(|| uuid::Uuid::new_v4().to_string().into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if this file needs an attachment i.e. it's not a file_id
|
/// Returns `true` if this file needs an attachment i.e. it's not a file_id
|
||||||
|
@ -255,12 +250,7 @@ impl Read {
|
||||||
fn new(it: Arc<TakeCell<dyn AsyncRead + Send + Unpin>>) -> Self {
|
fn new(it: Arc<TakeCell<dyn AsyncRead + Send + Unpin>>) -> Self {
|
||||||
let (tx, rx) = watch::channel(());
|
let (tx, rx) = watch::channel(());
|
||||||
|
|
||||||
Self {
|
Self { inner: it, buf: Arc::default(), notify: Arc::new(tx), wait: rx }
|
||||||
inner: it,
|
|
||||||
buf: Arc::default(),
|
|
||||||
notify: Arc::new(tx),
|
|
||||||
wait: rx,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn into_part(mut self, filename: Cow<'static, str>) -> Part {
|
pub(crate) async fn into_part(mut self, filename: Cow<'static, str>) -> Part {
|
||||||
|
|
|
@ -45,12 +45,7 @@ pub struct InputMediaPhoto {
|
||||||
|
|
||||||
impl InputMediaPhoto {
|
impl InputMediaPhoto {
|
||||||
pub const fn new(media: InputFile) -> Self {
|
pub const fn new(media: InputFile) -> Self {
|
||||||
Self {
|
Self { media, caption: None, parse_mode: None, caption_entities: None }
|
||||||
media,
|
|
||||||
caption: None,
|
|
||||||
parse_mode: None,
|
|
||||||
caption_entities: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn media(mut self, val: InputFile) -> Self {
|
pub fn media(mut self, val: InputFile) -> Self {
|
||||||
|
|
|
@ -28,10 +28,7 @@ impl KeyboardButton {
|
||||||
where
|
where
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { text: text.into(), request: None }
|
||||||
text: text.into(),
|
|
||||||
request: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request<T>(mut self, val: T) -> Self
|
pub fn request<T>(mut self, val: T) -> Self
|
||||||
|
@ -111,13 +108,8 @@ impl<'de> Deserialize<'de> for ButtonRequest {
|
||||||
{
|
{
|
||||||
let raw = RawRequest::deserialize(deserializer)?;
|
let raw = RawRequest::deserialize(deserializer)?;
|
||||||
match raw {
|
match raw {
|
||||||
RawRequest {
|
RawRequest { contact, location, poll, web_app }
|
||||||
contact,
|
if 1 < (contact.is_some() as u8
|
||||||
location,
|
|
||||||
poll,
|
|
||||||
web_app,
|
|
||||||
} if 1
|
|
||||||
< (contact.is_some() as u8
|
|
||||||
+ location.is_some() as u8
|
+ location.is_some() as u8
|
||||||
+ poll.is_some() as u8
|
+ poll.is_some() as u8
|
||||||
+ web_app.is_some() as u8) =>
|
+ web_app.is_some() as u8) =>
|
||||||
|
@ -127,20 +119,10 @@ impl<'de> Deserialize<'de> for ButtonRequest {
|
||||||
are mutually exclusive",
|
are mutually exclusive",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
RawRequest {
|
RawRequest { contact: Some(_), .. } => Ok(Self::Contact),
|
||||||
contact: Some(_), ..
|
RawRequest { location: Some(_), .. } => Ok(Self::Location),
|
||||||
} => Ok(Self::Contact),
|
RawRequest { poll: Some(poll_type), .. } => Ok(Self::Poll(poll_type)),
|
||||||
RawRequest {
|
RawRequest { web_app: Some(web_app), .. } => Ok(Self::WebApp(web_app)),
|
||||||
location: Some(_), ..
|
|
||||||
} => Ok(Self::Location),
|
|
||||||
RawRequest {
|
|
||||||
poll: Some(poll_type),
|
|
||||||
..
|
|
||||||
} => Ok(Self::Poll(poll_type)),
|
|
||||||
RawRequest {
|
|
||||||
web_app: Some(web_app),
|
|
||||||
..
|
|
||||||
} => Ok(Self::WebApp(web_app)),
|
|
||||||
|
|
||||||
_ => Err(D::Error::custom(
|
_ => Err(D::Error::custom(
|
||||||
"Either one of `request_contact`, `request_location`, `request_poll` and \
|
"Either one of `request_contact`, `request_location`, `request_poll` and \
|
||||||
|
@ -155,12 +137,7 @@ impl Serialize for ButtonRequest {
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
let mut raw = RawRequest {
|
let mut raw = RawRequest { contact: None, location: None, poll: None, web_app: None };
|
||||||
contact: None,
|
|
||||||
location: None,
|
|
||||||
poll: None,
|
|
||||||
web_app: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Contact => raw.contact = Some(True),
|
Self::Contact => raw.contact = Some(True),
|
||||||
|
@ -179,10 +156,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_no_request() {
|
fn serialize_no_request() {
|
||||||
let button = KeyboardButton {
|
let button = KeyboardButton { text: String::from(""), request: None };
|
||||||
text: String::from(""),
|
|
||||||
request: None,
|
|
||||||
};
|
|
||||||
let expected = r#"{"text":""}"#;
|
let expected = r#"{"text":""}"#;
|
||||||
let actual = serde_json::to_string(&button).unwrap();
|
let actual = serde_json::to_string(&button).unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
|
@ -190,10 +164,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_request_contact() {
|
fn serialize_request_contact() {
|
||||||
let button = KeyboardButton {
|
let button =
|
||||||
text: String::from(""),
|
KeyboardButton { text: String::from(""), request: Some(ButtonRequest::Contact) };
|
||||||
request: Some(ButtonRequest::Contact),
|
|
||||||
};
|
|
||||||
let expected = r#"{"text":"","request_contact":true}"#;
|
let expected = r#"{"text":"","request_contact":true}"#;
|
||||||
let actual = serde_json::to_string(&button).unwrap();
|
let actual = serde_json::to_string(&button).unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
|
@ -202,10 +174,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_no_request() {
|
fn deserialize_no_request() {
|
||||||
let json = r#"{"text":""}"#;
|
let json = r#"{"text":""}"#;
|
||||||
let expected = KeyboardButton {
|
let expected = KeyboardButton { text: String::from(""), request: None };
|
||||||
text: String::from(""),
|
|
||||||
request: None,
|
|
||||||
};
|
|
||||||
let actual = serde_json::from_str(json).unwrap();
|
let actual = serde_json::from_str(json).unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
@ -213,10 +182,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_request_contact() {
|
fn deserialize_request_contact() {
|
||||||
let json = r#"{"text":"","request_contact":true}"#;
|
let json = r#"{"text":"","request_contact":true}"#;
|
||||||
let expected = KeyboardButton {
|
let expected =
|
||||||
text: String::from(""),
|
KeyboardButton { text: String::from(""), request: Some(ButtonRequest::Contact) };
|
||||||
request: Some(ButtonRequest::Contact),
|
|
||||||
};
|
|
||||||
let actual = serde_json::from_str(json).unwrap();
|
let actual = serde_json::from_str(json).unwrap();
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,7 @@ impl LabeledPrice {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { label: label.into(), amount }
|
||||||
label: label.into(),
|
|
||||||
amount,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label<S>(mut self, val: S) -> Self
|
pub fn label<S>(mut self, val: S) -> Self
|
||||||
|
@ -51,10 +48,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize() {
|
fn serialize() {
|
||||||
let labeled_price = LabeledPrice {
|
let labeled_price = LabeledPrice { label: "Label".to_string(), amount: 60 };
|
||||||
label: "Label".to_string(),
|
|
||||||
amount: 60,
|
|
||||||
};
|
|
||||||
let expected = r#"{"label":"Label","amount":60}"#;
|
let expected = r#"{"label":"Label","amount":60}"#;
|
||||||
let actual = serde_json::to_string(&labeled_price).unwrap();
|
let actual = serde_json::to_string(&labeled_price).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
|
@ -36,12 +36,7 @@ pub enum MaskPoint {
|
||||||
|
|
||||||
impl MaskPosition {
|
impl MaskPosition {
|
||||||
pub const fn new(point: MaskPoint, x_shift: f64, y_shift: f64, scale: f64) -> Self {
|
pub const fn new(point: MaskPoint, x_shift: f64, y_shift: f64, scale: f64) -> Self {
|
||||||
Self {
|
Self { point, x_shift, y_shift, scale }
|
||||||
point,
|
|
||||||
x_shift,
|
|
||||||
y_shift,
|
|
||||||
scale,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn point(mut self, val: MaskPoint) -> Self {
|
pub const fn point(mut self, val: MaskPoint) -> Self {
|
||||||
|
|
|
@ -28,10 +28,7 @@ impl Me {
|
||||||
/// Returns the username of the bot.
|
/// Returns the username of the bot.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn username(&self) -> &str {
|
pub fn username(&self) -> &str {
|
||||||
self.user
|
self.user.username.as_deref().expect("Bots must have usernames")
|
||||||
.username
|
|
||||||
.as_deref()
|
|
||||||
.expect("Bots must have usernames")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a username mention of this bot.
|
/// Returns a username mention of this bot.
|
||||||
|
@ -79,9 +76,6 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(me.username(), "SomethingSomethingBot");
|
assert_eq!(me.username(), "SomethingSomethingBot");
|
||||||
assert_eq!(me.mention(), "@SomethingSomethingBot");
|
assert_eq!(me.mention(), "@SomethingSomethingBot");
|
||||||
assert_eq!(
|
assert_eq!(me.tme_url(), "https://t.me/SomethingSomethingBot".parse().unwrap());
|
||||||
me.tme_url(),
|
|
||||||
"https://t.me/SomethingSomethingBot".parse().unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -555,9 +555,7 @@ mod getters {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn author_signature(&self) -> Option<&str> {
|
pub fn author_signature(&self) -> Option<&str> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Common(MessageCommon {
|
Common(MessageCommon { author_signature, .. }) => author_signature.as_deref(),
|
||||||
author_signature, ..
|
|
||||||
}) => author_signature.as_deref(),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,45 +709,27 @@ mod getters {
|
||||||
pub fn caption_entities(&self) -> Option<&[MessageEntity]> {
|
pub fn caption_entities(&self) -> Option<&[MessageEntity]> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Common(MessageCommon {
|
Common(MessageCommon {
|
||||||
media_kind:
|
media_kind: MediaKind::Animation(MediaAnimation { caption_entities, .. }),
|
||||||
MediaKind::Animation(MediaAnimation {
|
|
||||||
caption_entities, ..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
| Common(MessageCommon {
|
| Common(MessageCommon {
|
||||||
media_kind:
|
media_kind: MediaKind::Audio(MediaAudio { caption_entities, .. }),
|
||||||
MediaKind::Audio(MediaAudio {
|
|
||||||
caption_entities, ..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
| Common(MessageCommon {
|
| Common(MessageCommon {
|
||||||
media_kind:
|
media_kind: MediaKind::Document(MediaDocument { caption_entities, .. }),
|
||||||
MediaKind::Document(MediaDocument {
|
|
||||||
caption_entities, ..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
| Common(MessageCommon {
|
| Common(MessageCommon {
|
||||||
media_kind:
|
media_kind: MediaKind::Photo(MediaPhoto { caption_entities, .. }),
|
||||||
MediaKind::Photo(MediaPhoto {
|
|
||||||
caption_entities, ..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
| Common(MessageCommon {
|
| Common(MessageCommon {
|
||||||
media_kind:
|
media_kind: MediaKind::Video(MediaVideo { caption_entities, .. }),
|
||||||
MediaKind::Video(MediaVideo {
|
|
||||||
caption_entities, ..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
| Common(MessageCommon {
|
| Common(MessageCommon {
|
||||||
media_kind:
|
media_kind: MediaKind::Voice(MediaVoice { caption_entities, .. }),
|
||||||
MediaKind::Voice(MediaVoice {
|
|
||||||
caption_entities, ..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
}) => Some(caption_entities),
|
}) => Some(caption_entities),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -978,9 +958,9 @@ mod getters {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn super_group_chat_created(&self) -> Option<True> {
|
pub fn super_group_chat_created(&self) -> Option<True> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
SupergroupChatCreated(MessageSupergroupChatCreated {
|
SupergroupChatCreated(MessageSupergroupChatCreated { supergroup_chat_created }) => {
|
||||||
supergroup_chat_created,
|
Some(*supergroup_chat_created)
|
||||||
}) => Some(*supergroup_chat_created),
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -988,9 +968,9 @@ mod getters {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn channel_chat_created(&self) -> Option<True> {
|
pub fn channel_chat_created(&self) -> Option<True> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ChannelChatCreated(MessageChannelChatCreated {
|
ChannelChatCreated(MessageChannelChatCreated { channel_chat_created }) => {
|
||||||
channel_chat_created,
|
Some(*channel_chat_created)
|
||||||
}) => Some(*channel_chat_created),
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -999,8 +979,7 @@ mod getters {
|
||||||
pub fn chat_migration(&self) -> Option<ChatMigration> {
|
pub fn chat_migration(&self) -> Option<ChatMigration> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Common(MessageCommon {
|
Common(MessageCommon {
|
||||||
media_kind: MediaKind::Migration(chat_migration),
|
media_kind: MediaKind::Migration(chat_migration), ..
|
||||||
..
|
|
||||||
}) => Some(*chat_migration),
|
}) => Some(*chat_migration),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -1101,10 +1080,7 @@ mod getters {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_automatic_forward(&self) -> bool {
|
pub fn is_automatic_forward(&self) -> bool {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Common(MessageCommon {
|
Common(MessageCommon { is_automatic_forward, .. }) => *is_automatic_forward,
|
||||||
is_automatic_forward,
|
|
||||||
..
|
|
||||||
}) => *is_automatic_forward,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1112,10 +1088,7 @@ mod getters {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn has_protected_content(&self) -> bool {
|
pub fn has_protected_content(&self) -> bool {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
Common(MessageCommon {
|
Common(MessageCommon { has_protected_content, .. }) => *has_protected_content,
|
||||||
has_protected_content,
|
|
||||||
..
|
|
||||||
}) => *has_protected_content,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1250,12 +1223,7 @@ impl Message {
|
||||||
/// supergroups).
|
/// supergroups).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn url_in_thread(&self, thread_starter_msg_id: MessageId) -> Option<Url> {
|
pub fn url_in_thread(&self, thread_starter_msg_id: MessageId) -> Option<Url> {
|
||||||
Self::url_in_thread_of(
|
Self::url_in_thread_of(self.chat.id, self.chat.username(), thread_starter_msg_id, self.id)
|
||||||
self.chat.id,
|
|
||||||
self.chat.username(),
|
|
||||||
thread_starter_msg_id,
|
|
||||||
self.id,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produces a direct link to a message in a given thread.
|
/// Produces a direct link to a message in a given thread.
|
||||||
|
@ -1300,9 +1268,7 @@ impl Message {
|
||||||
/// [`parse_caption_entities`]: Message::parse_caption_entities
|
/// [`parse_caption_entities`]: Message::parse_caption_entities
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn parse_entities(&self) -> Option<Vec<MessageEntityRef<'_>>> {
|
pub fn parse_entities(&self) -> Option<Vec<MessageEntityRef<'_>>> {
|
||||||
self.text()
|
self.text().zip(self.entities()).map(|(t, e)| MessageEntityRef::parse(t, e))
|
||||||
.zip(self.entities())
|
|
||||||
.map(|(t, e)| MessageEntityRef::parse(t, e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns message entities that represent text formatting.
|
/// Returns message entities that represent text formatting.
|
||||||
|
@ -1315,9 +1281,7 @@ impl Message {
|
||||||
/// [`parse_entities`]: Message::parse_entities
|
/// [`parse_entities`]: Message::parse_entities
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn parse_caption_entities(&self) -> Option<Vec<MessageEntityRef<'_>>> {
|
pub fn parse_caption_entities(&self) -> Option<Vec<MessageEntityRef<'_>>> {
|
||||||
self.caption()
|
self.caption().zip(self.caption_entities()).map(|(t, e)| MessageEntityRef::parse(t, e))
|
||||||
.zip(self.caption_entities())
|
|
||||||
.map(|(t, e)| MessageEntityRef::parse(t, e))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1610,10 +1574,7 @@ mod tests {
|
||||||
let message: Message = from_str(json).unwrap();
|
let message: Message = from_str(json).unwrap();
|
||||||
|
|
||||||
assert_eq!(message.chat.id, old);
|
assert_eq!(message.chat.id, old);
|
||||||
assert_eq!(
|
assert_eq!(message.chat_migration(), Some(ChatMigration::To { chat_id: new }));
|
||||||
message.chat_migration(),
|
|
||||||
Some(ChatMigration::To { chat_id: new })
|
|
||||||
);
|
|
||||||
assert_eq!(message.migrate_to_chat_id(), Some(new));
|
assert_eq!(message.migrate_to_chat_id(), Some(new));
|
||||||
|
|
||||||
// The user who initialized the migration
|
// The user who initialized the migration
|
||||||
|
@ -1624,10 +1585,7 @@ mod tests {
|
||||||
let message: Message = from_str(json).unwrap();
|
let message: Message = from_str(json).unwrap();
|
||||||
|
|
||||||
assert_eq!(message.chat.id, new);
|
assert_eq!(message.chat.id, new);
|
||||||
assert_eq!(
|
assert_eq!(message.chat_migration(), Some(ChatMigration::From { chat_id: old }));
|
||||||
message.chat_migration(),
|
|
||||||
Some(ChatMigration::From { chat_id: old })
|
|
||||||
);
|
|
||||||
assert_eq!(message.migrate_from_chat_id(), Some(old));
|
assert_eq!(message.migrate_from_chat_id(), Some(old));
|
||||||
|
|
||||||
// Anonymous bot
|
// Anonymous bot
|
||||||
|
|
|
@ -45,91 +45,55 @@ pub struct MessageEntityRef<'a> {
|
||||||
impl MessageEntity {
|
impl MessageEntity {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(kind: MessageEntityKind, offset: usize, length: usize) -> Self {
|
pub const fn new(kind: MessageEntityKind, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind, offset, length }
|
||||||
kind,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a bold text.
|
/// Create a message entity representing a bold text.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn bold(offset: usize, length: usize) -> Self {
|
pub const fn bold(offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Bold, offset, length }
|
||||||
kind: MessageEntityKind::Bold,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing an italic text.
|
/// Create a message entity representing an italic text.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn italic(offset: usize, length: usize) -> Self {
|
pub const fn italic(offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Italic, offset, length }
|
||||||
kind: MessageEntityKind::Italic,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing an underline text.
|
/// Create a message entity representing an underline text.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn underline(offset: usize, length: usize) -> Self {
|
pub const fn underline(offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Underline, offset, length }
|
||||||
kind: MessageEntityKind::Underline,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a strikethrough text.
|
/// Create a message entity representing a strikethrough text.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn strikethrough(offset: usize, length: usize) -> Self {
|
pub const fn strikethrough(offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Strikethrough, offset, length }
|
||||||
kind: MessageEntityKind::Strikethrough,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a spoiler text.
|
/// Create a message entity representing a spoiler text.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn spoiler(offset: usize, length: usize) -> Self {
|
pub const fn spoiler(offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Spoiler, offset, length }
|
||||||
kind: MessageEntityKind::Spoiler,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a monowidth text.
|
/// Create a message entity representing a monowidth text.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn code(offset: usize, length: usize) -> Self {
|
pub const fn code(offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Code, offset, length }
|
||||||
kind: MessageEntityKind::Code,
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a monowidth block.
|
/// Create a message entity representing a monowidth block.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn pre(language: Option<String>, offset: usize, length: usize) -> Self {
|
pub const fn pre(language: Option<String>, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::Pre { language }, offset, length }
|
||||||
kind: MessageEntityKind::Pre { language },
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a clickable text URL.
|
/// Create a message entity representing a clickable text URL.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn text_link(url: reqwest::Url, offset: usize, length: usize) -> Self {
|
pub const fn text_link(url: reqwest::Url, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::TextLink { url }, offset, length }
|
||||||
kind: MessageEntityKind::TextLink { url },
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a text mention.
|
/// Create a message entity representing a text mention.
|
||||||
|
@ -140,32 +104,20 @@ impl MessageEntity {
|
||||||
/// [`MessageEntity::text_mention_id`] instead.
|
/// [`MessageEntity::text_mention_id`] instead.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn text_mention(user: User, offset: usize, length: usize) -> Self {
|
pub const fn text_mention(user: User, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::TextMention { user }, offset, length }
|
||||||
kind: MessageEntityKind::TextMention { user },
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a text link in the form of
|
/// Create a message entity representing a text link in the form of
|
||||||
/// `tg://user/?id=...` that mentions user with `user_id`.
|
/// `tg://user/?id=...` that mentions user with `user_id`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn text_mention_id(user_id: UserId, offset: usize, length: usize) -> Self {
|
pub fn text_mention_id(user_id: UserId, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::TextLink { url: user_id.url() }, offset, length }
|
||||||
kind: MessageEntityKind::TextLink { url: user_id.url() },
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a message entity representing a custom emoji.
|
/// Create a message entity representing a custom emoji.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn custom_emoji(custom_emoji_id: String, offset: usize, length: usize) -> Self {
|
pub const fn custom_emoji(custom_emoji_id: String, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self { kind: MessageEntityKind::CustomEmoji { custom_emoji_id }, offset, length }
|
||||||
kind: MessageEntityKind::CustomEmoji { custom_emoji_id },
|
|
||||||
offset,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -242,11 +194,7 @@ impl<'a> MessageEntityRef<'a> {
|
||||||
// This creates entities with **wrong** offsets (UTF-16) that we later patch.
|
// This creates entities with **wrong** offsets (UTF-16) that we later patch.
|
||||||
let mut entities: Vec<_> = entities
|
let mut entities: Vec<_> = entities
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| Self {
|
.map(|e| Self { message: text, range: e.offset..e.offset + e.length, kind: &e.kind })
|
||||||
message: text,
|
|
||||||
range: e.offset..e.offset + e.length,
|
|
||||||
kind: &e.kind,
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Convert offsets
|
// Convert offsets
|
||||||
|
@ -254,12 +202,7 @@ impl<'a> MessageEntityRef<'a> {
|
||||||
// References to all offsets that need patching
|
// References to all offsets that need patching
|
||||||
let mut offsets: Vec<&mut usize> = entities
|
let mut offsets: Vec<&mut usize> = entities
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.flat_map(
|
.flat_map(|Self { range: Range { start, end }, .. }| [start, end])
|
||||||
|Self {
|
|
||||||
range: Range { start, end },
|
|
||||||
..
|
|
||||||
}| [start, end],
|
|
||||||
)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Sort in decreasing order, so the smallest elements are at the end and can be
|
// Sort in decreasing order, so the smallest elements are at the end and can be
|
||||||
|
@ -276,11 +219,7 @@ impl<'a> MessageEntityRef<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch all offsets that can be patched
|
// Patch all offsets that can be patched
|
||||||
while offsets
|
while offsets.last().map(|&&mut offset| offset <= len_utf16).unwrap_or(false) {
|
||||||
.last()
|
|
||||||
.map(|&&mut offset| offset <= len_utf16)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
let offset = offsets.pop().unwrap();
|
let offset = offsets.pop().unwrap();
|
||||||
assert_eq!(*offset, len_utf16, "Invalid utf-16 offset");
|
assert_eq!(*offset, len_utf16, "Invalid utf-16 offset");
|
||||||
|
|
||||||
|
@ -352,9 +291,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MessageEntity {
|
MessageEntity {
|
||||||
kind: MessageEntityKind::Pre {
|
kind: MessageEntityKind::Pre { language: Some("rust".to_string()) },
|
||||||
language: Some("rust".to_string())
|
|
||||||
},
|
|
||||||
offset: 1,
|
offset: 1,
|
||||||
length: 2,
|
length: 2,
|
||||||
},
|
},
|
||||||
|
@ -385,26 +322,10 @@ mod tests {
|
||||||
let parsed = MessageEntityRef::parse(
|
let parsed = MessageEntityRef::parse(
|
||||||
"быба",
|
"быба",
|
||||||
&[
|
&[
|
||||||
MessageEntity {
|
MessageEntity { kind: Strikethrough, offset: 0, length: 1 },
|
||||||
kind: Strikethrough,
|
MessageEntity { kind: Bold, offset: 1, length: 1 },
|
||||||
offset: 0,
|
MessageEntity { kind: Italic, offset: 2, length: 1 },
|
||||||
length: 1,
|
MessageEntity { kind: Code, offset: 3, length: 1 },
|
||||||
},
|
|
||||||
MessageEntity {
|
|
||||||
kind: Bold,
|
|
||||||
offset: 1,
|
|
||||||
length: 1,
|
|
||||||
},
|
|
||||||
MessageEntity {
|
|
||||||
kind: Italic,
|
|
||||||
offset: 2,
|
|
||||||
length: 1,
|
|
||||||
},
|
|
||||||
MessageEntity {
|
|
||||||
kind: Code,
|
|
||||||
offset: 3,
|
|
||||||
length: 1,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -424,11 +345,7 @@ mod tests {
|
||||||
fn parse_symbol_24bit() {
|
fn parse_symbol_24bit() {
|
||||||
let parsed = MessageEntityRef::parse(
|
let parsed = MessageEntityRef::parse(
|
||||||
"xx আ #tt",
|
"xx আ #tt",
|
||||||
&[MessageEntity {
|
&[MessageEntity { kind: Hashtag, offset: 5, length: 3 }],
|
||||||
kind: Hashtag,
|
|
||||||
offset: 5,
|
|
||||||
length: 3,
|
|
||||||
}],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
|
@ -443,21 +360,9 @@ mod tests {
|
||||||
"b i b",
|
"b i b",
|
||||||
// For some reason this is how telegram encodes <b>b <i>i<i/> b<b/>
|
// For some reason this is how telegram encodes <b>b <i>i<i/> b<b/>
|
||||||
&[
|
&[
|
||||||
MessageEntity {
|
MessageEntity { kind: Bold, offset: 0, length: 2 },
|
||||||
kind: Bold,
|
MessageEntity { kind: Bold, offset: 2, length: 3 },
|
||||||
offset: 0,
|
MessageEntity { kind: Italic, offset: 2, length: 1 },
|
||||||
length: 2,
|
|
||||||
},
|
|
||||||
MessageEntity {
|
|
||||||
kind: Bold,
|
|
||||||
offset: 2,
|
|
||||||
length: 3,
|
|
||||||
},
|
|
||||||
MessageEntity {
|
|
||||||
kind: Italic,
|
|
||||||
offset: 2,
|
|
||||||
length: 1,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -484,16 +389,8 @@ mod tests {
|
||||||
let parsed = MessageEntityRef::parse(
|
let parsed = MessageEntityRef::parse(
|
||||||
"",
|
"",
|
||||||
&[
|
&[
|
||||||
MessageEntity {
|
MessageEntity { kind: Bold, offset: 0, length: 0 },
|
||||||
kind: Bold,
|
MessageEntity { kind: Italic, offset: 0, length: 0 },
|
||||||
offset: 0,
|
|
||||||
length: 0,
|
|
||||||
},
|
|
||||||
MessageEntity {
|
|
||||||
kind: Italic,
|
|
||||||
offset: 0,
|
|
||||||
length: 0,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,7 @@ impl PassportElementError {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { message: message.into(), kind }
|
||||||
message: message.into(),
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message<S>(mut self, val: S) -> Self
|
pub fn message<S>(mut self, val: S) -> Self
|
||||||
|
@ -98,11 +95,7 @@ impl PassportElementErrorDataField {
|
||||||
S1: Into<String>,
|
S1: Into<String>,
|
||||||
S2: Into<String>,
|
S2: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, field_name: field_name.into(), data_hash: data_hash.into() }
|
||||||
r#type,
|
|
||||||
field_name: field_name.into(),
|
|
||||||
data_hash: data_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -149,10 +142,7 @@ impl PassportElementErrorFrontSide {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hash: file_hash.into() }
|
||||||
r#type,
|
|
||||||
file_hash: file_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -191,10 +181,7 @@ impl PassportElementErrorReverseSide {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hash: file_hash.into() }
|
||||||
r#type,
|
|
||||||
file_hash: file_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -231,10 +218,7 @@ impl PassportElementErrorSelfie {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hash: file_hash.into() }
|
||||||
r#type,
|
|
||||||
file_hash: file_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -272,10 +256,7 @@ impl PassportElementErrorFile {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hash: file_hash.into() }
|
||||||
r#type,
|
|
||||||
file_hash: file_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -313,10 +294,7 @@ impl PassportElementErrorFiles {
|
||||||
where
|
where
|
||||||
S: IntoIterator<Item = String>,
|
S: IntoIterator<Item = String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hashes: file_hashes.into_iter().collect() }
|
||||||
r#type,
|
|
||||||
file_hashes: file_hashes.into_iter().collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -355,10 +333,7 @@ impl PassportElementErrorTranslationFile {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hash: file_hash.into() }
|
||||||
r#type,
|
|
||||||
file_hash: file_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -396,10 +371,7 @@ impl PassportElementErrorTranslationFiles {
|
||||||
where
|
where
|
||||||
S: IntoIterator<Item = String>,
|
S: IntoIterator<Item = String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, file_hashes: file_hashes.into_iter().collect() }
|
||||||
r#type,
|
|
||||||
file_hashes: file_hashes.into_iter().collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -437,10 +409,7 @@ impl PassportElementErrorUnspecified {
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { r#type, element_hash: file_hash.into() }
|
||||||
r#type,
|
|
||||||
element_hash: file_hash.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -29,11 +29,7 @@ mod tests {
|
||||||
let json = r#"{"file_id":"id","file_unique_id":"","width":320,"height":320,
|
let json = r#"{"file_id":"id","file_unique_id":"","width":320,"height":320,
|
||||||
"file_size":3452}"#;
|
"file_size":3452}"#;
|
||||||
let expected = PhotoSize {
|
let expected = PhotoSize {
|
||||||
file: FileMeta {
|
file: FileMeta { id: "id".to_owned(), unique_id: "".to_owned(), size: 3452 },
|
||||||
id: "id".to_owned(),
|
|
||||||
unique_id: "".to_owned(),
|
|
||||||
size: 3452,
|
|
||||||
},
|
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,11 +55,7 @@ impl KeyboardMarkup {
|
||||||
K::Item: IntoIterator<Item = KeyboardButton>,
|
K::Item: IntoIterator<Item = KeyboardButton>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
keyboard: keyboard
|
keyboard: keyboard.into_iter().map(<_>::into_iter).map(<_>::collect).collect(),
|
||||||
.into_iter()
|
|
||||||
.map(<_>::into_iter)
|
|
||||||
.map(<_>::collect)
|
|
||||||
.collect(),
|
|
||||||
resize_keyboard: None,
|
resize_keyboard: None,
|
||||||
one_time_keyboard: None,
|
one_time_keyboard: None,
|
||||||
input_field_placeholder: None,
|
input_field_placeholder: None,
|
||||||
|
|
|
@ -40,10 +40,7 @@ pub struct KeyboardRemove {
|
||||||
impl KeyboardRemove {
|
impl KeyboardRemove {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self { remove_keyboard: True, selective: None }
|
||||||
remove_keyboard: True,
|
|
||||||
selective: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -24,11 +24,7 @@ impl ShippingOption {
|
||||||
S2: Into<String>,
|
S2: Into<String>,
|
||||||
P: IntoIterator<Item = LabeledPrice>,
|
P: IntoIterator<Item = LabeledPrice>,
|
||||||
{
|
{
|
||||||
Self {
|
Self { id: id.into(), title: title.into(), prices: prices.into_iter().collect() }
|
||||||
id: id.into(),
|
|
||||||
title: title.into(),
|
|
||||||
prices: prices.into_iter().collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id<S>(mut self, val: S) -> Self
|
pub fn id<S>(mut self, val: S) -> Self
|
||||||
|
@ -65,10 +61,7 @@ mod tests {
|
||||||
let shipping_option = ShippingOption {
|
let shipping_option = ShippingOption {
|
||||||
id: "0".to_string(),
|
id: "0".to_string(),
|
||||||
title: "Option".to_string(),
|
title: "Option".to_string(),
|
||||||
prices: vec![LabeledPrice {
|
prices: vec![LabeledPrice { label: "Label".to_string(), amount: 60 }],
|
||||||
label: "Label".to_string(),
|
|
||||||
amount: 60,
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
let expected = r#"{"id":"0","title":"Option","prices":[{"label":"Label","amount":60}]}"#;
|
let expected = r#"{"id":"0","title":"Option","prices":[{"label":"Label","amount":60}]}"#;
|
||||||
let actual = serde_json::to_string(&shipping_option).unwrap();
|
let actual = serde_json::to_string(&shipping_option).unwrap();
|
||||||
|
|
|
@ -289,10 +289,7 @@ impl TryFrom<StickerFormatRaw> for StickerFormat {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn try_from(
|
fn try_from(
|
||||||
StickerFormatRaw {
|
StickerFormatRaw { is_animated, is_video }: StickerFormatRaw,
|
||||||
is_animated,
|
|
||||||
is_video,
|
|
||||||
}: StickerFormatRaw,
|
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let ret = match (is_animated, is_video) {
|
let ret = match (is_animated, is_video) {
|
||||||
(false, false) => Self::Raster,
|
(false, false) => Self::Raster,
|
||||||
|
@ -308,18 +305,9 @@ impl TryFrom<StickerFormatRaw> for StickerFormat {
|
||||||
impl From<StickerFormat> for StickerFormatRaw {
|
impl From<StickerFormat> for StickerFormatRaw {
|
||||||
fn from(kind: StickerFormat) -> Self {
|
fn from(kind: StickerFormat) -> Self {
|
||||||
match kind {
|
match kind {
|
||||||
StickerFormat::Raster => Self {
|
StickerFormat::Raster => Self { is_animated: false, is_video: false },
|
||||||
is_animated: false,
|
StickerFormat::Animated => Self { is_animated: true, is_video: false },
|
||||||
is_video: false,
|
StickerFormat::Video => Self { is_animated: false, is_video: true },
|
||||||
},
|
|
||||||
StickerFormat::Animated => Self {
|
|
||||||
is_animated: true,
|
|
||||||
is_video: false,
|
|
||||||
},
|
|
||||||
StickerFormat::Video => Self {
|
|
||||||
is_animated: false,
|
|
||||||
is_video: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,10 +161,9 @@ impl<'de> Deserialize<'de> for UpdateKind {
|
||||||
|
|
||||||
if let Ok(Some(k)) = k {
|
if let Ok(Some(k)) = k {
|
||||||
let res = match k {
|
let res = match k {
|
||||||
"message" => map
|
"message" => {
|
||||||
.next_value::<Message>()
|
map.next_value::<Message>().map(UpdateKind::Message).map_err(|_| false)
|
||||||
.map(UpdateKind::Message)
|
}
|
||||||
.map_err(|_| false),
|
|
||||||
"edited_message" => map
|
"edited_message" => map
|
||||||
.next_value::<Message>()
|
.next_value::<Message>()
|
||||||
.map(UpdateKind::EditedMessage)
|
.map(UpdateKind::EditedMessage)
|
||||||
|
@ -197,10 +196,7 @@ impl<'de> Deserialize<'de> for UpdateKind {
|
||||||
.next_value::<PreCheckoutQuery>()
|
.next_value::<PreCheckoutQuery>()
|
||||||
.map(UpdateKind::PreCheckoutQuery)
|
.map(UpdateKind::PreCheckoutQuery)
|
||||||
.map_err(|_| false),
|
.map_err(|_| false),
|
||||||
"poll" => map
|
"poll" => map.next_value::<Poll>().map(UpdateKind::Poll).map_err(|_| false),
|
||||||
.next_value::<Poll>()
|
|
||||||
.map(UpdateKind::Poll)
|
|
||||||
.map_err(|_| false),
|
|
||||||
"poll_answer" => map
|
"poll_answer" => map
|
||||||
.next_value::<PollAnswer>()
|
.next_value::<PollAnswer>()
|
||||||
.map(UpdateKind::PollAnswer)
|
.map(UpdateKind::PollAnswer)
|
||||||
|
|
|
@ -66,11 +66,7 @@ impl User {
|
||||||
/// Returns `None` if `self.username.is_none()`.
|
/// Returns `None` if `self.username.is_none()`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn tme_url(&self) -> Option<reqwest::Url> {
|
pub fn tme_url(&self) -> Option<reqwest::Url> {
|
||||||
Some(
|
Some(format!("https://t.me/{}", self.username.as_ref()?).parse().unwrap())
|
||||||
format!("https://t.me/{}", self.username.as_ref()?)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an URL that links to this user in the form of `t.me/<...>` or
|
/// Returns an URL that links to this user in the form of `t.me/<...>` or
|
||||||
|
@ -190,19 +186,10 @@ mod tests {
|
||||||
assert_eq!(user_a.mention(), Some("@aaaaaaaaaaaaaaaa".to_owned()));
|
assert_eq!(user_a.mention(), Some("@aaaaaaaaaaaaaaaa".to_owned()));
|
||||||
assert_eq!(user_b.mention(), None);
|
assert_eq!(user_b.mention(), None);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(user_a.tme_url(), Some("https://t.me/aaaaaaaaaaaaaaaa".parse().unwrap()));
|
||||||
user_a.tme_url(),
|
|
||||||
Some("https://t.me/aaaaaaaaaaaaaaaa".parse().unwrap())
|
|
||||||
);
|
|
||||||
assert_eq!(user_b.tme_url(), None);
|
assert_eq!(user_b.tme_url(), None);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(user_a.preferably_tme_url(), "https://t.me/aaaaaaaaaaaaaaaa".parse().unwrap());
|
||||||
user_a.preferably_tme_url(),
|
assert_eq!(user_b.preferably_tme_url(), "tg://user/?id=44".parse().unwrap());
|
||||||
"https://t.me/aaaaaaaaaaaaaaaa".parse().unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
user_b.preferably_tme_url(),
|
|
||||||
"tg://user/?id=44".parse().unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,9 +61,7 @@ mod tests {
|
||||||
/// Test that `UserId` is serialized as the underlying integer
|
/// Test that `UserId` is serialized as the underlying integer
|
||||||
#[test]
|
#[test]
|
||||||
fn deser() {
|
fn deser() {
|
||||||
let user_id = S {
|
let user_id = S { user_id: UserId(17) };
|
||||||
user_id: UserId(17),
|
|
||||||
};
|
|
||||||
let json = r#"{"user_id":17}"#;
|
let json = r#"{"user_id":17}"#;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
format_code_in_doc_comments = true
|
|
||||||
wrap_comments = true
|
|
||||||
format_strings = true
|
|
||||||
max_width = 80
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
use_small_heuristics = "Max"
|
|
||||||
use_field_init_shorthand = true
|
|
|
@ -19,8 +19,7 @@ pub(crate) fn fold_attrs<A, R>(
|
||||||
.filter(|&a| filter(a))
|
.filter(|&a| filter(a))
|
||||||
.flat_map(|attribute| {
|
.flat_map(|attribute| {
|
||||||
// FIXME: don't allocate here
|
// FIXME: don't allocate here
|
||||||
let attrs =
|
let attrs = match attribute.parse_args_with(|input: &ParseBuffer| {
|
||||||
match attribute.parse_args_with(|input: &ParseBuffer| {
|
|
||||||
input.parse_terminated::<_, Token![,]>(Attr::parse)
|
input.parse_terminated::<_, Token![,]>(Attr::parse)
|
||||||
}) {
|
}) {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => ok,
|
||||||
|
@ -75,10 +74,7 @@ impl Parse for Attr {
|
||||||
|
|
||||||
impl Attr {
|
impl Attr {
|
||||||
pub(crate) fn span(&self) -> Span {
|
pub(crate) fn span(&self) -> Span {
|
||||||
self.key
|
self.key.span().join(self.value.span()).unwrap_or_else(|| self.key.span())
|
||||||
.span()
|
|
||||||
.join(self.value.span())
|
|
||||||
.unwrap_or_else(|| self.key.span())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,16 +95,9 @@ impl AttrValue {
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn expect<T>(
|
pub fn expect<T>(self, expected: &str, f: impl FnOnce(Self) -> Result<T, Self>) -> Result<T> {
|
||||||
self,
|
|
||||||
expected: &str,
|
|
||||||
f: impl FnOnce(Self) -> Result<T, Self>,
|
|
||||||
) -> Result<T> {
|
|
||||||
f(self).map_err(|this| {
|
f(self).map_err(|this| {
|
||||||
compile_error_at(
|
compile_error_at(&format!("expected {expected}, found {}", this.descr()), this.span())
|
||||||
&format!("expected {expected}, found {}", this.descr()),
|
|
||||||
this.span(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
command::Command, command_enum::CommandEnum, compile_error,
|
command::Command, command_enum::CommandEnum, compile_error, fields_parse::impl_parse_args,
|
||||||
fields_parse::impl_parse_args, unzip::Unzip, Result,
|
unzip::Unzip, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
@ -15,17 +15,12 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
.map(|variant| {
|
.map(|variant| {
|
||||||
let command = Command::new(
|
let command = Command::new(&variant.ident.to_string(), &variant.attrs, &command_enum)?;
|
||||||
&variant.ident.to_string(),
|
|
||||||
&variant.attrs,
|
|
||||||
&command_enum,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
let self_variant = quote! { Self::#variant_name };
|
let self_variant = quote! { Self::#variant_name };
|
||||||
|
|
||||||
let parse =
|
let parse = impl_parse_args(&variant.fields, self_variant, &command.parser);
|
||||||
impl_parse_args(&variant.fields, self_variant, &command.parser);
|
|
||||||
|
|
||||||
Ok((parse, command))
|
Ok((parse, command))
|
||||||
})
|
})
|
||||||
|
@ -48,10 +43,7 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_commands(infos: &[Command]) -> proc_macro2::TokenStream {
|
fn impl_commands(infos: &[Command]) -> proc_macro2::TokenStream {
|
||||||
let commands = infos
|
let commands = infos.iter().filter(|command| command.description_is_enabled()).map(|command| {
|
||||||
.iter()
|
|
||||||
.filter(|command| command.description_is_enabled())
|
|
||||||
.map(|command| {
|
|
||||||
let c = command.get_prefixed_command();
|
let c = command.get_prefixed_command();
|
||||||
let d = command.description.as_deref().unwrap_or_default();
|
let d = command.description.as_deref().unwrap_or_default();
|
||||||
quote! { BotCommand::new(#c,#d) }
|
quote! { BotCommand::new(#c,#d) }
|
||||||
|
@ -65,10 +57,7 @@ fn impl_commands(infos: &[Command]) -> proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_descriptions(
|
fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::TokenStream {
|
||||||
infos: &[Command],
|
|
||||||
global: &CommandEnum,
|
|
||||||
) -> proc_macro2::TokenStream {
|
|
||||||
let command_descriptions = infos
|
let command_descriptions = infos
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|command| command.description_is_enabled())
|
.filter(|command| command.description_is_enabled())
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
command_attr::CommandAttrs, command_enum::CommandEnum,
|
command_attr::CommandAttrs, command_enum::CommandEnum, error::compile_error_at,
|
||||||
error::compile_error_at, fields_parse::ParserType, Result,
|
fields_parse::ParserType, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct Command {
|
pub(crate) struct Command {
|
||||||
|
@ -43,13 +43,9 @@ impl Command {
|
||||||
(None, None) => global_options.rename_rule.apply(name),
|
(None, None) => global_options.rename_rule.apply(name),
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix = prefix
|
let prefix = prefix.map(|(p, _)| p).unwrap_or_else(|| global_options.prefix.clone());
|
||||||
.map(|(p, _)| p)
|
|
||||||
.unwrap_or_else(|| global_options.prefix.clone());
|
|
||||||
let description = description.map(|(d, _)| d);
|
let description = description.map(|(d, _)| d);
|
||||||
let parser = parser
|
let parser = parser.map(|(p, _)| p).unwrap_or_else(|| global_options.parser_type.clone());
|
||||||
.map(|(p, _)| p)
|
|
||||||
.unwrap_or_else(|| global_options.parser_type.clone());
|
|
||||||
|
|
||||||
Ok(Self { prefix, description, parser, name })
|
Ok(Self { prefix, description, parser, name })
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,19 +60,13 @@ impl CommandAttrs {
|
||||||
separator: None,
|
separator: None,
|
||||||
},
|
},
|
||||||
|mut this, attr| {
|
|mut this, attr| {
|
||||||
fn insert<T>(
|
fn insert<T>(opt: &mut Option<(T, Span)>, x: T, sp: Span) -> Result<()> {
|
||||||
opt: &mut Option<(T, Span)>,
|
|
||||||
x: T,
|
|
||||||
sp: Span,
|
|
||||||
) -> Result<()> {
|
|
||||||
match opt {
|
match opt {
|
||||||
slot @ None => {
|
slot @ None => {
|
||||||
*slot = Some((x, sp));
|
*slot = Some((x, sp));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => Err(compile_error_at("duplicate attribute", sp)),
|
||||||
Err(compile_error_at("duplicate attribute", sp))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,18 +94,16 @@ impl CommandAttr {
|
||||||
let kind = match &*key.to_string() {
|
let kind = match &*key.to_string() {
|
||||||
"prefix" => Prefix(value.expect_string()?),
|
"prefix" => Prefix(value.expect_string()?),
|
||||||
"description" => Description(value.expect_string()?),
|
"description" => Description(value.expect_string()?),
|
||||||
"rename_rule" => RenameRule(
|
"rename_rule" => {
|
||||||
value
|
RenameRule(value.expect_string().and_then(|r| self::RenameRule::parse(&r))?)
|
||||||
.expect_string()
|
}
|
||||||
.and_then(|r| self::RenameRule::parse(&r))?,
|
|
||||||
),
|
|
||||||
"rename" => Rename(value.expect_string()?),
|
"rename" => Rename(value.expect_string()?),
|
||||||
"parse_with" => ParseWith(ParserType::parse(value)?),
|
"parse_with" => ParseWith(ParserType::parse(value)?),
|
||||||
"separator" => Separator(value.expect_string()?),
|
"separator" => Separator(value.expect_string()?),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(compile_error_at(
|
return Err(compile_error_at(
|
||||||
"unexpected attribute name (expected one of `prefix`, \
|
"unexpected attribute name (expected one of `prefix`, `description`, \
|
||||||
`description`, `rename`, `parse_with` and `separator`",
|
`rename`, `parse_with` and `separator`",
|
||||||
key.span(),
|
key.span(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
command_attr::CommandAttrs, error::compile_error_at,
|
command_attr::CommandAttrs, error::compile_error_at, fields_parse::ParserType,
|
||||||
fields_parse::ParserType, rename_rules::RenameRule, Result,
|
rename_rules::RenameRule, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct CommandEnum {
|
pub(crate) struct CommandEnum {
|
||||||
|
@ -13,14 +13,7 @@ pub(crate) struct CommandEnum {
|
||||||
impl CommandEnum {
|
impl CommandEnum {
|
||||||
pub fn from_attributes(attributes: &[syn::Attribute]) -> Result<Self> {
|
pub fn from_attributes(attributes: &[syn::Attribute]) -> Result<Self> {
|
||||||
let attrs = CommandAttrs::from_attributes(attributes)?;
|
let attrs = CommandAttrs::from_attributes(attributes)?;
|
||||||
let CommandAttrs {
|
let CommandAttrs { prefix, description, rename_rule, rename, parser, separator } = attrs;
|
||||||
prefix,
|
|
||||||
description,
|
|
||||||
rename_rule,
|
|
||||||
rename,
|
|
||||||
parser,
|
|
||||||
separator,
|
|
||||||
} = attrs;
|
|
||||||
|
|
||||||
if let Some((_rename, sp)) = rename {
|
if let Some((_rename, sp)) = rename {
|
||||||
return Err(compile_error_at(
|
return Err(compile_error_at(
|
||||||
|
@ -32,18 +25,14 @@ impl CommandEnum {
|
||||||
let mut parser = parser.map(|(p, _)| p).unwrap_or(ParserType::Default);
|
let mut parser = parser.map(|(p, _)| p).unwrap_or(ParserType::Default);
|
||||||
|
|
||||||
// FIXME: Error on unused separator
|
// FIXME: Error on unused separator
|
||||||
if let (ParserType::Split { separator }, Some((s, _))) =
|
if let (ParserType::Split { separator }, Some((s, _))) = (&mut parser, &separator) {
|
||||||
(&mut parser, &separator)
|
|
||||||
{
|
|
||||||
*separator = Some(s.clone())
|
*separator = Some(s.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
prefix: prefix.map(|(p, _)| p).unwrap_or_else(|| "/".to_owned()),
|
prefix: prefix.map(|(p, _)| p).unwrap_or_else(|| "/".to_owned()),
|
||||||
description: description.map(|(d, _)| d),
|
description: description.map(|(d, _)| d),
|
||||||
rename_rule: rename_rule
|
rename_rule: rename_rule.map(|(rr, _)| rr).unwrap_or(RenameRule::Identity),
|
||||||
.map(|(rr, _)| rr)
|
|
||||||
.unwrap_or(RenameRule::Identity),
|
|
||||||
parser_type: parser,
|
parser_type: parser,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_error_at(msg: &str, sp: Span) -> Error {
|
pub(crate) fn compile_error_at(msg: &str, sp: Span) -> Error {
|
||||||
use proc_macro2::{
|
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, TokenTree};
|
||||||
Delimiter, Group, Ident, Literal, Punct, Spacing, TokenTree,
|
|
||||||
};
|
|
||||||
// compile_error! { $msg }
|
// compile_error! { $msg }
|
||||||
let ts = TokenStream::from_iter(vec![
|
let ts = TokenStream::from_iter(vec![
|
||||||
TokenTree::Ident(Ident::new("compile_error", sp)),
|
TokenTree::Ident(Ident::new("compile_error", sp)),
|
||||||
|
|
|
@ -12,9 +12,7 @@ pub(crate) enum ParserType {
|
||||||
|
|
||||||
impl ParserType {
|
impl ParserType {
|
||||||
pub fn parse(value: AttrValue) -> Result<Self> {
|
pub fn parse(value: AttrValue) -> Result<Self> {
|
||||||
value.expect(
|
value.expect(r#""default", "split", or a path to a custom parser function"#, |v| match v {
|
||||||
r#""default", "split", or a path to a custom parser function"#,
|
|
||||||
|v| match v {
|
|
||||||
AttrValue::Path(p) => Ok(ParserType::Custom(p)),
|
AttrValue::Path(p) => Ok(ParserType::Custom(p)),
|
||||||
AttrValue::Lit(syn::Lit::Str(ref l)) => match &*l.value() {
|
AttrValue::Lit(syn::Lit::Str(ref l)) => match &*l.value() {
|
||||||
"default" => Ok(ParserType::Default),
|
"default" => Ok(ParserType::Default),
|
||||||
|
@ -22,8 +20,7 @@ impl ParserType {
|
||||||
_ => Err(v),
|
_ => Err(v),
|
||||||
},
|
},
|
||||||
_ => Err(v),
|
_ => Err(v),
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,12 +31,8 @@ pub(crate) fn impl_parse_args(
|
||||||
) -> proc_macro2::TokenStream {
|
) -> proc_macro2::TokenStream {
|
||||||
match fields {
|
match fields {
|
||||||
Fields::Unit => self_variant,
|
Fields::Unit => self_variant,
|
||||||
Fields::Unnamed(fields) => {
|
Fields::Unnamed(fields) => impl_parse_args_unnamed(fields, self_variant, parser),
|
||||||
impl_parse_args_unnamed(fields, self_variant, parser)
|
Fields::Named(named) => impl_parse_args_named(named, self_variant, parser),
|
||||||
}
|
|
||||||
Fields::Named(named) => {
|
|
||||||
impl_parse_args_named(named, self_variant, parser)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +41,7 @@ pub(crate) fn impl_parse_args_unnamed(
|
||||||
variant: proc_macro2::TokenStream,
|
variant: proc_macro2::TokenStream,
|
||||||
parser_type: &ParserType,
|
parser_type: &ParserType,
|
||||||
) -> proc_macro2::TokenStream {
|
) -> proc_macro2::TokenStream {
|
||||||
let get_arguments =
|
let get_arguments = create_parser(parser_type, data.unnamed.iter().map(|f| &f.ty));
|
||||||
create_parser(parser_type, data.unnamed.iter().map(|f| &f.ty));
|
|
||||||
let iter = (0..data.unnamed.len()).map(syn::Index::from);
|
let iter = (0..data.unnamed.len()).map(syn::Index::from);
|
||||||
let mut initialization = quote! {};
|
let mut initialization = quote! {};
|
||||||
for i in iter {
|
for i in iter {
|
||||||
|
@ -69,8 +61,7 @@ pub(crate) fn impl_parse_args_named(
|
||||||
variant: proc_macro2::TokenStream,
|
variant: proc_macro2::TokenStream,
|
||||||
parser_type: &ParserType,
|
parser_type: &ParserType,
|
||||||
) -> proc_macro2::TokenStream {
|
) -> proc_macro2::TokenStream {
|
||||||
let get_arguments =
|
let get_arguments = create_parser(parser_type, data.named.iter().map(|f| &f.ty));
|
||||||
create_parser(parser_type, data.named.iter().map(|f| &f.ty));
|
|
||||||
let i = (0..).map(syn::Index::from);
|
let i = (0..).map(syn::Index::from);
|
||||||
let name = data.named.iter().map(|f| f.ident.as_ref().unwrap());
|
let name = data.named.iter().map(|f| f.ident.as_ref().unwrap());
|
||||||
let res = quote! {
|
let res = quote! {
|
||||||
|
@ -105,10 +96,9 @@ fn create_parser<'a>(
|
||||||
quote! { compile_error!("Default parser works only with exactly 1 field") }
|
quote! { compile_error!("Default parser works only with exactly 1 field") }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ParserType::Split { separator } => parser_with_separator(
|
ParserType::Split { separator } => {
|
||||||
&separator.clone().unwrap_or_else(|| " ".to_owned()),
|
parser_with_separator(&separator.clone().unwrap_or_else(|| " ".to_owned()), types)
|
||||||
types,
|
}
|
||||||
),
|
|
||||||
ParserType::Custom(path) => quote! { #path },
|
ParserType::Custom(path) => quote! { #path },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
use crate::error::{compile_error, Result};
|
use crate::error::{compile_error, Result};
|
||||||
|
|
||||||
use heck::{
|
use heck::{
|
||||||
ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase,
|
ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase,
|
||||||
ToShoutySnakeCase, ToSnakeCase,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -65,10 +64,9 @@ impl RenameRule {
|
||||||
"identity" => Identity,
|
"identity" => Identity,
|
||||||
invalid => {
|
invalid => {
|
||||||
return Err(compile_error(format!(
|
return Err(compile_error(format!(
|
||||||
"invalid rename rule `{invalid}` (supported rules: \
|
"invalid rename rule `{invalid}` (supported rules: `lowercase`, `UPPERCASE`, \
|
||||||
`lowercase`, `UPPERCASE`, `PascalCase`, `camelCase`, \
|
`PascalCase`, `camelCase`, `snake_case`, `SCREAMING_SNAKE_CASE`, \
|
||||||
`snake_case`, `SCREAMING_SNAKE_CASE`, `kebab-case`, \
|
`kebab-case`, `SCREAMING-KEBAB-CASE` and `identity`)"
|
||||||
`SCREAMING-KEBAB-CASE` and `identity`)"
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,42 +62,23 @@ fn many_attributes() {
|
||||||
Help,
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("!start", "").unwrap());
|
||||||
DefaultCommands::Start,
|
assert_eq!(DefaultCommands::descriptions().to_string(), "!start — desc\n/help");
|
||||||
DefaultCommands::parse("!start", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::descriptions().to_string(),
|
|
||||||
"!start — desc\n/help"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn global_attributes() {
|
fn global_attributes() {
|
||||||
#[derive(BotCommands, Debug, PartialEq)]
|
#[derive(BotCommands, Debug, PartialEq)]
|
||||||
#[command(
|
#[command(prefix = "!", rename_rule = "lowercase", description = "Bot commands")]
|
||||||
prefix = "!",
|
|
||||||
rename_rule = "lowercase",
|
|
||||||
description = "Bot commands"
|
|
||||||
)]
|
|
||||||
enum DefaultCommands {
|
enum DefaultCommands {
|
||||||
#[command(prefix = "/")]
|
#[command(prefix = "/")]
|
||||||
Start,
|
Start,
|
||||||
Help,
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("/start", "MyNameBot").unwrap());
|
||||||
DefaultCommands::Start,
|
assert_eq!(DefaultCommands::Help, DefaultCommands::parse("!help", "MyNameBot").unwrap());
|
||||||
DefaultCommands::parse("/start", "MyNameBot").unwrap()
|
assert_eq!(DefaultCommands::descriptions().to_string(), "Bot commands\n\n/start\n!help");
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::Help,
|
|
||||||
DefaultCommands::parse("!help", "MyNameBot").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::descriptions().to_string(),
|
|
||||||
"Bot commands\n\n/start\n!help"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -153,25 +134,15 @@ fn parse_custom_parser() {
|
||||||
mod parser {
|
mod parser {
|
||||||
use teloxide::utils::command::ParseError;
|
use teloxide::utils::command::ParseError;
|
||||||
|
|
||||||
pub fn custom_parse_function(
|
pub fn custom_parse_function(s: String) -> Result<(u8, String), ParseError> {
|
||||||
s: String,
|
|
||||||
) -> Result<(u8, String), ParseError> {
|
|
||||||
let vec = s.split_whitespace().collect::<Vec<_>>();
|
let vec = s.split_whitespace().collect::<Vec<_>>();
|
||||||
let (left, right) = match vec.as_slice() {
|
let (left, right) = match vec.as_slice() {
|
||||||
[l, r] => (l, r),
|
[l, r] => (l, r),
|
||||||
_ => {
|
_ => return Err(ParseError::IncorrectFormat("might be 2 arguments!".into())),
|
||||||
return Err(ParseError::IncorrectFormat(
|
|
||||||
"might be 2 arguments!".into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
left.parse::<u8>().map(|res| (res, (*right).to_string())).map_err(
|
left.parse::<u8>().map(|res| (res, (*right).to_string())).map_err(|_| {
|
||||||
|_| {
|
ParseError::Custom("First argument must be a integer!".to_owned().into())
|
||||||
ParseError::Custom(
|
})
|
||||||
"First argument must be a integer!".to_owned().into(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,46 +224,18 @@ fn rename_rules() {
|
||||||
Foo,
|
Foo,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(DefaultCommands::AaaAaa, DefaultCommands::parse("/aaaaaa", "").unwrap());
|
||||||
DefaultCommands::AaaAaa,
|
assert_eq!(DefaultCommands::BbbBbb, DefaultCommands::parse("/BBBBBB", "").unwrap());
|
||||||
DefaultCommands::parse("/aaaaaa", "").unwrap()
|
assert_eq!(DefaultCommands::CccCcc, DefaultCommands::parse("/CccCcc", "").unwrap());
|
||||||
);
|
assert_eq!(DefaultCommands::DddDdd, DefaultCommands::parse("/dddDdd", "").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(DefaultCommands::EeeEee, DefaultCommands::parse("/eee_eee", "").unwrap());
|
||||||
DefaultCommands::BbbBbb,
|
assert_eq!(DefaultCommands::FffFff, DefaultCommands::parse("/FFF_FFF", "").unwrap());
|
||||||
DefaultCommands::parse("/BBBBBB", "").unwrap()
|
assert_eq!(DefaultCommands::GggGgg, DefaultCommands::parse("/ggg-ggg", "").unwrap());
|
||||||
);
|
assert_eq!(DefaultCommands::HhhHhh, DefaultCommands::parse("/HHH-HHH", "").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(DefaultCommands::Foo, DefaultCommands::parse("/Bar", "").unwrap());
|
||||||
DefaultCommands::CccCcc,
|
|
||||||
DefaultCommands::parse("/CccCcc", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::DddDdd,
|
|
||||||
DefaultCommands::parse("/dddDdd", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::EeeEee,
|
|
||||||
DefaultCommands::parse("/eee_eee", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::FffFff,
|
|
||||||
DefaultCommands::parse("/FFF_FFF", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::GggGgg,
|
|
||||||
DefaultCommands::parse("/ggg-ggg", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::HhhHhh,
|
|
||||||
DefaultCommands::parse("/HHH-HHH", "").unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DefaultCommands::Foo,
|
|
||||||
DefaultCommands::parse("/Bar", "").unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/\
|
"/aaaaaa\n/BBBBBB\n/CccCcc\n/dddDdd\n/eee_eee\n/FFF_FFF\n/ggg-ggg\n/HHH-HHH\n/Bar",
|
||||||
HHH-HHH\n/Bar",
|
|
||||||
DefaultCommands::descriptions().to_string()
|
DefaultCommands::descriptions().to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,3 +4,4 @@ format_strings = true
|
||||||
imports_granularity = "Crate"
|
imports_granularity = "Crate"
|
||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
use_field_init_shorthand = true
|
use_field_init_shorthand = true
|
||||||
|
merge_derives = false
|
||||||
|
|
Loading…
Reference in a new issue