-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
api/backend: better time serialize/deserialize (#533)
* api: serialize as timestamp milliseconds * api: serialize as time duration instead of u32
- Loading branch information
Showing
33 changed files
with
289 additions
and
157 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,50 @@ | ||
use nghe_proc_macro::api_derive; | ||
use serde_with::{serde_as, TimestampMilliSeconds}; | ||
use time::OffsetDateTime; | ||
use uuid::Uuid; | ||
|
||
#[api_derive] | ||
#[api_derive(serde_as = true)] | ||
#[endpoint(path = "scrobble")] | ||
#[cfg_attr(test, derive(Default))] | ||
pub struct Request { | ||
#[serde(rename = "id")] | ||
pub ids: Vec<Uuid>, | ||
#[serde(rename = "time")] | ||
pub times: Option<Vec<u64>>, | ||
#[serde_as(as = "Option<Vec<TimestampMilliSeconds<i64>>>")] | ||
pub times: Option<Vec<OffsetDateTime>>, | ||
pub submission: Option<bool>, | ||
} | ||
|
||
#[api_derive] | ||
pub struct Response; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use rstest::rstest; | ||
use time::macros::datetime; | ||
use uuid::uuid; | ||
|
||
use super::*; | ||
|
||
#[rstest] | ||
#[case( | ||
"id=d4ea6896-a838-446c-ace4-d9d13d336391", | ||
Some(Request { | ||
ids: vec![uuid!("d4ea6896-a838-446c-ace4-d9d13d336391")], | ||
..Default::default() | ||
}) | ||
)] | ||
#[case( | ||
"id=d4ea6896-a838-446c-ace4-d9d13d336391&\ | ||
time=1000000000000", | ||
Some(Request { | ||
ids: vec![uuid!("d4ea6896-a838-446c-ace4-d9d13d336391")], | ||
times: Some(vec![datetime!(2001-09-09 01:46:40.000 UTC)]), | ||
..Default::default() | ||
}) | ||
)] | ||
fn test_deserialize(#[case] url: &str, #[case] request: Option<Request>) { | ||
serde_html_form::from_str::<Request>(url).unwrap(); | ||
assert_eq!(serde_html_form::from_str::<Request>(url).ok(), request); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
pub mod serde { | ||
use ::serde::{de, ser, Deserialize, Deserializer, Serializer}; | ||
use num_traits::ToPrimitive; | ||
use time::Duration; | ||
|
||
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
{ | ||
serializer.serialize_u32( | ||
duration | ||
.as_seconds_f32() | ||
.ceil() | ||
.to_u32() | ||
.ok_or_else(|| ser::Error::custom("Could not serialize duration to integer"))?, | ||
) | ||
} | ||
|
||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
Ok(Duration::seconds_f32( | ||
<u32>::deserialize(deserializer)? | ||
.to_f32() | ||
.ok_or_else(|| de::Error::custom("Could not deserialize duration from integer"))?, | ||
)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
use std::iter::Sum; | ||
use std::ops::Add; | ||
|
||
use diesel::sql_types::Float; | ||
use diesel::{AsExpression, FromSqlRow}; | ||
|
||
use crate::Error; | ||
|
||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, AsExpression, FromSqlRow)] | ||
#[repr(transparent)] | ||
#[diesel(sql_type = Float)] | ||
#[cfg_attr(test, derive(fake::Dummy))] | ||
pub struct Duration(pub time::Duration); | ||
|
||
impl From<time::Duration> for Duration { | ||
fn from(value: time::Duration) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
impl From<Duration> for time::Duration { | ||
fn from(value: Duration) -> Self { | ||
value.0 | ||
} | ||
} | ||
|
||
impl From<f32> for Duration { | ||
fn from(value: f32) -> Self { | ||
time::Duration::seconds_f32(value).into() | ||
} | ||
} | ||
|
||
impl From<Duration> for f32 { | ||
fn from(value: Duration) -> Self { | ||
value.0.as_seconds_f32() | ||
} | ||
} | ||
|
||
impl TryFrom<std::time::Duration> for Duration { | ||
type Error = Error; | ||
|
||
fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> { | ||
time::Duration::try_from(value).map_err(Self::Error::from).map(Self::from) | ||
} | ||
} | ||
|
||
impl Add<Duration> for Duration { | ||
type Output = Self; | ||
|
||
fn add(self, rhs: Duration) -> Self::Output { | ||
(self.0 + rhs.0).into() | ||
} | ||
} | ||
|
||
impl Sum<Duration> for Duration { | ||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { | ||
iter.fold(Self::default(), Self::add) | ||
} | ||
} | ||
|
||
pub trait Trait { | ||
fn duration(&self) -> Duration; | ||
} | ||
|
||
impl Trait for Duration { | ||
fn duration(&self) -> Duration { | ||
*self | ||
} | ||
} | ||
|
||
impl<D: Trait> Trait for Vec<D> { | ||
fn duration(&self) -> Duration { | ||
self.iter().map(D::duration).sum() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use rstest::rstest; | ||
|
||
use super::*; | ||
|
||
#[rstest] | ||
#[case(&[], 0.0)] | ||
#[case(&[100.2, 200.3], 300.5)] | ||
fn test_sum(#[case] durations: &[f32], #[case] result: f32) { | ||
// Allow microsecond mismatch. | ||
assert!( | ||
(f32::from(durations.iter().copied().map(Duration::from).sum::<Duration>()) - result) | ||
.abs() | ||
< 1e-6 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.