Skip to content

Commit

Permalink
Make duration optional
Browse files Browse the repository at this point in the history
Fixes #1
  • Loading branch information
d-k-bo committed Jul 22, 2024
1 parent 56ac70a commit 2851722
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- BREAKING: change `duration` from `Duration` to `Option<Duration>` because some entries (e.g. livestreams) don't have a duration

## [0.3.1] - 2024-05-23

### Added
Expand Down
22 changes: 21 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ impl<'client> IntoFuture for MediathekQueryBuilder<'client> {
mod tests {
use crate::{
models::{Query, QueryField},
MediathekQuery,
Mediathek, MediathekQuery,
};

#[test]
Expand Down Expand Up @@ -433,4 +433,24 @@ mod tests {
}
);
}

#[tokio::test]
async fn test_query() -> Result<(), Box<dyn std::error::Error>> {
let mediathek = Mediathek::new(
format!(
"{} Test Suite ({})",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_REPOSITORY")
)
.parse()
.unwrap(),
)?;

mediathek.query([QueryField::Topic], "tagesschau").await?;

// livestreams return `""` as the duration
mediathek.query([QueryField::Topic], "livestream").await?;

Ok(())
}
}
48 changes: 40 additions & 8 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ pub struct Item {
#[serde(deserialize_with = "empty_string_as_none")]
pub description: Option<String>,
pub timestamp: i64,
#[serde(with = "duration_secs")]
pub duration: Duration,
#[serde(with = "optional_duration_secs")]
pub duration: Option<Duration>,
pub size: Option<usize>,
pub url_website: String,
#[serde(deserialize_with = "empty_string_as_none")]
Expand Down Expand Up @@ -145,23 +145,55 @@ mod duration_millisecs {
}
}

mod duration_secs {
mod optional_duration_secs {
use std::time::Duration;

use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{Deserializer, Serialize, Serializer};

pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
pub fn serialize<S>(duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
duration.as_secs().serialize(serializer)
duration
.as_ref()
.map(Duration::as_secs)
.serialize(serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
<u64>::deserialize(deserializer).map(Duration::from_secs)
struct OptionalDurationVisitor;

impl<'de> serde::de::Visitor<'de> for OptionalDurationVisitor {
type Value = Option<Duration>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "an integer or an empty string")
}

fn visit_u64<E: serde::de::Error>(self, n: u64) -> Result<Self::Value, E> {
Ok(Some(Duration::from_secs(n)))
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}

fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
if s.is_empty() {
Ok(None)
} else {
Err(E::custom("string is not empty"))
}
}
}

deserializer.deserialize_any(OptionalDurationVisitor)
}
}

Expand Down

0 comments on commit 2851722

Please sign in to comment.