Use Duration over UInt for timeouts and similar fields

This commit is contained in:
iinuwa 2020-03-20 05:29:51 -05:00 committed by GitHub
parent 7307c695e0
commit 122097cfef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 203 additions and 16 deletions

View File

@ -24,6 +24,17 @@ Breaking changes:
* Remove deprecated endpoint `r0::contact::create_contact` (deprecated in r0.6.0)
* Add lazy-loading options to `r0::filter::RoomEventFilter` (introduced in r0.5.0)
* Change type for `limit` request parameter of `r0::context::get_context` from `u8` to `Option<js_int::UInt>`
* Use `std::time::Duration` for appropriate fields on several endpoints:
```
r0::{
account::request_openid_token,
keys::{claim_keys, get_keys},
presence::get_presence,
sync::sync_events,
typing::create_typing_event,
voip::get_turn_server_info
}
```
# 0.6.0

View File

@ -14,7 +14,7 @@ edition = "2018"
[dependencies]
http = "0.2.0"
js_int = { version = "0.1.2", features = ["serde"] }
js_int = { version = "0.1.3", features = ["serde"] }
ruma-api = "0.14.0"
ruma-events = "0.17.0"
ruma-identifiers = "0.14.1"

View File

@ -11,3 +11,5 @@
pub mod error;
pub mod r0;
pub mod unversioned;
mod serde;

View File

@ -1,6 +1,7 @@
//! [POST /_matrix/client/r0/user/{userId}/openid/request_token](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-user-userid-openid-request-token)
use js_int::UInt;
use std::time::Duration;
use ruma_api::ruma_api;
use ruma_identifiers::UserId;
use serde::{Deserialize, Serialize};
@ -29,7 +30,8 @@ ruma_api! {
/// Homeserver domain for verification of user's identity.
pub matrix_server_name: String,
/// Seconds until token expiration.
pub expires_in: UInt,
#[serde(with = "crate::serde::duration::secs")]
pub expires_in: Duration,
}
}

View File

@ -2,7 +2,8 @@
use std::collections::HashMap;
use js_int::UInt;
use std::time::Duration;
use ruma_api::ruma_api;
use ruma_identifiers::{DeviceId, UserId};
use serde_json::Value;
@ -23,7 +24,8 @@ ruma_api! {
/// The time (in milliseconds) to wait when downloading keys from remote servers.
/// 10 seconds is the recommended default.
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<UInt>,
#[serde(default, with = "crate::serde::duration::opt_ms")]
pub timeout: Option<Duration>,
/// The keys to be claimed.
pub one_time_keys: HashMap<UserId, HashMap<DeviceId, KeyAlgorithm>>,

View File

@ -1,8 +1,7 @@
//! [POST /_matrix/client/r0/keys/query](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-keys-query)
use std::collections::HashMap;
use std::{collections::HashMap, time::Duration};
use js_int::UInt;
use ruma_api::ruma_api;
use ruma_identifiers::{DeviceId, UserId};
use serde_json::Value;
@ -23,7 +22,8 @@ ruma_api! {
/// The time (in milliseconds) to wait when downloading keys from remote servers.
/// 10 seconds is the recommended default.
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<UInt>,
#[serde(default, with = "crate::serde::duration::opt_ms")]
pub timeout: Option<Duration>,
/// The keys to be downloaded. An empty list indicates all devices for the corresponding user.
pub device_keys: HashMap<UserId, Vec<DeviceId>>,

View File

@ -1,6 +1,7 @@
//! [GET /_matrix/client/r0/presence/{userId}/status](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status)
use js_int::UInt;
use std::time::Duration;
use ruma_api::ruma_api;
use ruma_events::presence::PresenceState;
use ruma_identifiers::UserId;
@ -30,7 +31,8 @@ ruma_api! {
pub currently_active: Option<bool>,
/// The length of time in milliseconds since an action was performed by the user.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_active_ago: Option<UInt>,
#[serde(default, with = "crate::serde::duration::opt_ms")]
pub last_active_ago: Option<Duration>,
/// The user's presence state.
pub presence: PresenceState,
}

View File

@ -1,6 +1,6 @@
//! [GET /_matrix/client/r0/sync](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-sync)
use std::collections::HashMap;
use std::{collections::HashMap, time::Duration};
use js_int::UInt;
use ruma_api::{ruma_api, Outgoing};
@ -48,8 +48,9 @@ ruma_api! {
pub set_presence: Option<SetPresence>,
/// The maximum time to poll in milliseconds before returning this request.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "crate::serde::duration::opt_ms")]
#[ruma_api(query)]
pub timeout: Option<UInt>,
pub timeout: Option<Duration>,
}
response {

View File

@ -1,6 +1,7 @@
//! [PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}](https://matrix.org/docs/spec/client_server/r0.4.0.html#put-matrix-client-r0-rooms-roomid-typing-userid)
use js_int::UInt;
use std::time::Duration;
use ruma_api::ruma_api;
use ruma_identifiers::{RoomId, UserId};
@ -20,7 +21,8 @@ ruma_api! {
pub room_id: RoomId,
/// The length of time in milliseconds to mark this user as typing.
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<UInt>,
#[serde(default, with = "crate::serde::duration::opt_ms")]
pub timeout: Option<Duration>,
/// Whether the user is typing or not. If `false`, the `timeout` key can be omitted.
pub typing: bool,
/// The user who has started to type.

View File

@ -1,6 +1,7 @@
//! [GET /_matrix/client/r0/voip/turnServer](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-voip-turnserver)
use js_int::UInt;
use std::time::Duration;
use ruma_api::ruma_api;
ruma_api! {
@ -19,7 +20,8 @@ ruma_api! {
/// The password to use.
pub password: String,
/// The time-to-live in seconds.
pub ttl: UInt,
#[serde(with = "crate::serde::duration::secs")]
pub ttl: Duration,
/// A list of TURN URIs.
pub uris: Vec<String>,
/// The username to use.

3
src/serde.rs Normal file
View File

@ -0,0 +1,3 @@
//! Modules to hold functions for de-/serializing remote types
pub mod duration;

4
src/serde/duration.rs Normal file
View File

@ -0,0 +1,4 @@
//! De-/serialization functions for `std::time::Duration` objects
pub mod opt_ms;
pub mod secs;

View File

@ -0,0 +1,90 @@
//! De-/serialization functions for `Option<std::time::Duration>` objects represented as milliseconds.
//! Delegates to `js_int::UInt` to ensure integer size is within bounds.
use std::{convert::TryFrom, time::Duration};
use js_int::UInt;
use serde::{
de::{Deserialize, Deserializer},
ser::{Error, Serialize, Serializer},
};
/// Serialize an Option<Duration>.
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn serialize<S>(opt_duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match opt_duration {
Some(duration) => match UInt::try_from(duration.as_millis()) {
Ok(uint) => uint.serialize(serializer),
Err(err) => Err(S::Error::custom(err)),
},
None => serializer.serialize_none(),
}
}
/// Deserializes an Option<Duration>.
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Option::<UInt>::deserialize(deserializer)?
.map(|millis| Duration::from_millis(millis.into())))
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct DurationTest {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default, with = "crate::serde::duration::opt_ms")]
timeout: Option<Duration>,
}
#[test]
fn test_deserialize_some_duration_as_milliseconds() {
let json = json!({ "timeout": 3000 });
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest {
timeout: Some(Duration::from_millis(3000))
},
);
}
#[test]
fn test_deserialize_empty_duration_as_milliseconds() {
let json = json!({});
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest { timeout: None },
);
}
#[test]
fn test_serialize_some_duration_as_milliseconds() {
let request = DurationTest {
timeout: Some(Duration::new(2, 0)),
};
assert_eq!(
serde_json::to_value(&request).unwrap(),
json!({ "timeout": 2000 })
);
}
#[test]
fn test_serialize_empty_duration_as_milliseconds() {
let request = DurationTest { timeout: None };
assert_eq!(serde_json::to_value(&request).unwrap(), json!({}));
}
}

View File

@ -0,0 +1,66 @@
//! De-/serialization functions for `Option<std::time::Duration>` objects represented as milliseconds.
//! Delegates to `js_int::UInt` to ensure integer size is within bounds.
use std::{convert::TryFrom, time::Duration};
use js_int::UInt;
use serde::{
de::{Deserialize, Deserializer},
ser::{Error, Serialize, Serializer},
};
/// Serializes a Duration to an integer representing seconds.
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match UInt::try_from(duration.as_secs()) {
Ok(uint) => uint.serialize(serializer),
Err(err) => Err(S::Error::custom(err)),
}
}
/// Deserializes an integer representing seconds into a Duration.
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
UInt::deserialize(deserializer).map(|secs| Duration::from_secs(secs.into()))
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct DurationTest {
#[serde(with = "crate::serde::duration::secs")]
timeout: Duration,
}
#[test]
fn test_deserialize_duration_as_seconds() {
let json = json!({ "timeout": 3 });
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest {
timeout: Duration::from_secs(3)
},
);
}
#[test]
fn test_serialize_duration_as_seconds() {
let test = DurationTest {
timeout: Duration::from_millis(7000),
};
assert_eq!(serde_json::to_value(test).unwrap(), json!({ "timeout": 7 }),);
}
}