#[derive(Debug, Clone, PartialEq, Hash, Eq, Default)]
pub enum Status {
#[default]
Published,
Drafted,
Deleted,
Expired,
#[cfg(feature = "experimental_publish_delay")]
DelayedPublish(super::PublishDelay),
Other(String),
}
impl serde::Serialize for Status {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_string().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for Status {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer)
.and_then(|status_str| status_str.parse().map_err(serde::de::Error::custom))
}
}
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Status::Published => f.write_str("published"),
Status::Drafted => f.write_str("draft"),
Status::Deleted => f.write_str("deleted"),
Status::Expired => f.write_str("expired"),
#[cfg(feature = "experimental_publish_delay")]
Status::DelayedPublish(delay) => write!(f, "published+{}", delay),
Status::Other(status) => f.write_str(status),
}
}
}
impl std::str::FromStr for Status {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let status_str = s.trim().trim_matches('"');
if status_str.is_empty() {
return Ok(Self::Published);
}
#[cfg(feature = "experimental_publish_delay")]
if let Some((status_part, delay_part)) = status_str.split_once('+') {
if status_part.to_lowercase() == "published" {
let delay = delay_part.parse::<super::PublishDelay>()?;
return Ok(Self::DelayedPublish(delay));
}
return Err(crate::Error::InvalidPostStatus(format!(
"only 'published' can have a delay suffix, got '{}'",
status_part
)));
}
let status_lower = status_str.to_lowercase();
match status_lower.as_str() {
"published" => Ok(Self::Published),
"draft" => Ok(Self::Drafted),
"deleted" => Ok(Self::Deleted),
"expired" => Ok(Self::Expired),
other => Ok(Self::Other(other.to_string())),
}
}
}
#[test]
fn post_status() {
use std::str::FromStr;
assert_eq!(Some(Status::Drafted), Status::from_str("draft").ok());
assert_eq!(Some(Status::Published), Status::from_str("published").ok());
assert_eq!(Some(Status::Published), "published".parse().ok());
assert_eq!(Some(Status::Published), Status::from_str("PublisHed").ok());
assert_eq!(Some(Status::Published), "PublisHed".parse().ok());
}
#[cfg(test)]
#[cfg(feature = "experimental_publish_delay")]
mod publish_delay_tests {
use super::*;
#[test]
fn delayed_publish_parsing() {
let status: Status = "published+PT3M".parse().unwrap();
assert!(matches!(status, Status::DelayedPublish(_)));
}
#[test]
fn delayed_publish_display() {
let status: Status = "published+PT3M".parse().unwrap();
assert_eq!(status.to_string(), "published+PT3M");
}
#[test]
fn delayed_publish_serialization() {
let status: Status = "published+PT1H".parse().unwrap();
assert_eq!(
serde_json::to_string(&status).unwrap(),
"\"published+PT1H\""
);
}
#[test]
fn delayed_publish_deserialization() {
let status: Status = serde_json::from_str("\"published+PT30M\"").unwrap();
assert!(matches!(status, Status::DelayedPublish(_)));
}
#[test]
fn delayed_publish_invalid_suffix() {
let result: Result<Status, _> = "draft+PT3M".parse();
assert!(result.is_err());
}
#[test]
fn delayed_publish_invalid_duration() {
let result: Result<Status, _> = "published+invalid".parse();
assert!(result.is_err());
}
}