/// Represents the potential statuses a post can take.
///
/// More information about this can be found at <https://indieweb.org/post_status>.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Hash, Eq)]
#[serde(untagged, rename_all = "kebab-case")]
#[derive(Default)]
pub enum Status {
    /// The content is available for general use.
    #[default]
    Published,
    /// The content is not yet ready for general use.
    Drafted,
    /// The content has been considered "deleted".
    Deleted,
    /// The content has expired from general use.
    Expired,
    /// A custom status that posts can have.
    Other(String),
}

impl std::fmt::Display for Status {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Status::Published => "published",
            Status::Drafted => "draft",
            Status::Deleted => "deleted",
            Status::Expired => "expired",
            Status::Other(status) => status.as_str(),
        })
    }
}

impl std::str::FromStr for Status {
    type Err = crate::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            // NOTE: This is the default publish state according to the spec.
            return Ok(Self::Published);
        }

        match s.to_lowercase().trim_matches('"') {
            "published" => Ok(Self::Published),
            "draft" => Ok(Self::Drafted),
            "deleted" => Ok(Self::Deleted),
            "expired" => Ok(Self::Expired),
            invalid => Err(Self::Err::InvalidPostStatus(invalid.to_string())),
        }
    }
}

#[test]
fn post_status() {
    use std::str::FromStr;
    assert_eq!(Some(Status::Drafted), Status::from_str("draft").ok());
}
