From af0dd99ef48f9035418a8ed6ca94759595337736 Mon Sep 17 00:00:00 2001 From: Maybe Waffle <waffle.lapkin@gmail.com> Date: Sun, 10 Apr 2022 18:46:52 +0400 Subject: [PATCH] Use `Duration` instead of `u32` as the `RetryAfter` field --- CHANGELOG.md | 2 +- src/adaptors/throttle/request.rs | 11 ++----- src/errors.rs | 8 ++--- src/types.rs | 50 ++++++++++++++++++++++++++++++++ src/types/response_parameters.rs | 6 ++-- 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d819d83..5afb3b4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improve `Throttling` adoptor - Freeze when getting `RetryAfter(_)` error - Retry requests that previously returned `RetryAfter(_)` error -- `RequestError::RetryAfter` now has a `u32` field instead of `i32` +- `RequestError::RetryAfter` now has a `Duration` field instead of `i32` ### Added diff --git a/src/adaptors/throttle/request.rs b/src/adaptors/throttle/request.rs index a1a78afd..c109b178 100644 --- a/src/adaptors/throttle/request.rs +++ b/src/adaptors/throttle/request.rs @@ -1,9 +1,4 @@ -use std::{ - future::Future, - pin::Pin, - sync::Arc, - time::{Duration, Instant}, -}; +use std::{future::Future, pin::Pin, sync::Arc, time::Instant}; use futures::{ future::BoxFuture, @@ -188,7 +183,7 @@ where let retry_after = res.as_ref().err().and_then(<_>::retry_after); if let Some(retry_after) = retry_after { - let after = Duration::from_secs(retry_after.into()); + let after = retry_after; let until = Instant::now() + after; // If we'll retry, we check that worker hasn't died at the start of the loop @@ -196,7 +191,7 @@ where let _ = freeze.send(FreezeUntil { until, after, chat }).await; if retry { - log::warn!("Freezing, before retrying: {}", retry_after); + log::warn!("Freezing, before retrying: {:?}", retry_after); tokio::time::sleep_until(until.into()).await; } } diff --git a/src/errors.rs b/src/errors.rs index 2ed570db..5d8c4c1b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,4 +1,4 @@ -use std::io; +use std::{io, time::Duration}; use serde::Deserialize; use thiserror::Error; @@ -19,8 +19,8 @@ pub enum RequestError { /// In case of exceeding flood control, the number of seconds left to wait /// before the request can be repeated. - #[error("Retry after {0} seconds")] - RetryAfter(u32), + #[error("Retry after {0:?}")] + RetryAfter(Duration), /// Network error while sending a request to Telegram. #[error("A network error: {0}")] @@ -62,7 +62,7 @@ pub enum DownloadError { pub trait AsResponseParameters { fn response_parameters(&self) -> Option<ResponseParameters>; - fn retry_after(&self) -> Option<u32> { + fn retry_after(&self) -> Option<Duration> { self.response_parameters().and_then(|rp| match rp { ResponseParameters::RetryAfter(n) => Some(n), _ => None, diff --git a/src/types.rs b/src/types.rs index ea1f33c0..e544656c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -345,3 +345,53 @@ pub(crate) mod option_url_from_string { } } } + +pub(crate) mod duration_secs { + use std::time::Duration; + + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub(crate) fn serialize<S>(this: &Duration, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + // match this { + // Some(url) => url.serialize(serializer), + // None => "".serialize(serializer), + // } + this.as_secs().serialize(serializer) + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error> + where + D: Deserializer<'de>, + { + u64::deserialize(deserializer).map(Duration::from_secs) + } + + #[test] + fn test() { + #[derive(Serialize, Deserialize)] + struct Struct { + #[serde(with = "crate::types::duration_secs")] + duration: Duration, + } + + { + let json = r#"{"duration":0}"#; + let duration: Struct = serde_json::from_str(json).unwrap(); + assert_eq!(duration.duration, Duration::from_secs(0)); + assert_eq!(serde_json::to_string(&duration).unwrap(), json.to_owned()); + + let json = r#"{"duration":12}"#; + let duration: Struct = serde_json::from_str(json).unwrap(); + assert_eq!(duration.duration, Duration::from_secs(12)); + assert_eq!(serde_json::to_string(&duration).unwrap(), json.to_owned()); + + let json = r#"{"duration":1234}"#; + let duration: Struct = serde_json::from_str(json).unwrap(); + assert_eq!(duration.duration, Duration::from_secs(1234)); + assert_eq!(serde_json::to_string(&duration).unwrap(), json.to_owned()); + } + } +} diff --git a/src/types/response_parameters.rs b/src/types/response_parameters.rs index 88f3a355..b792a626 100644 --- a/src/types/response_parameters.rs +++ b/src/types/response_parameters.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use serde::{Deserialize, Serialize}; /// Contains information about why a request was unsuccessful. @@ -16,7 +18,7 @@ pub enum ResponseParameters { /// In case of exceeding flood control, the number of seconds left to wait /// before the request can be repeated. - RetryAfter(u32), + RetryAfter(#[serde(with = "crate::types::duration_secs")] Duration), } #[cfg(test)] @@ -34,7 +36,7 @@ mod tests { #[test] fn retry_after_deserialization() { - let expected = ResponseParameters::RetryAfter(123_456); + let expected = ResponseParameters::RetryAfter(Duration::from_secs(123_456)); let actual: ResponseParameters = serde_json::from_str(r#"{"retry_after":123456}"#).unwrap(); assert_eq!(expected, actual);