use serde::{Deserialize, Serialize};
use crate::error::Error;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Anchor {
Newest,
Oldest,
FirstUnread,
#[serde(untagged)]
MessageId(u64),
}
impl Anchor {
pub const NEWEST: &str = "newest";
pub const OLDEST: &str = "oldest";
pub const FIRST_UNREAD: &str = "first_unread";
pub const fn new() -> Self {
Self::Newest
}
pub fn try_from_str(val: &str) -> Result<Self, Error> {
match val.to_lowercase().as_str() {
Self::NEWEST => Ok(Self::Newest),
Self::OLDEST => Ok(Self::Oldest),
Self::FIRST_UNREAD => Ok(Self::FirstUnread),
s => s
.parse::<u64>()
.map(Self::MessageId)
.map_err(|err| Error::io_other(format!("{err}"))),
}
}
}
impl Default for Anchor {
fn default() -> Self {
Self::new()
}
}
impl From<Anchor> for String {
fn from(val: Anchor) -> Self {
val.to_string()
}
}
impl From<&Anchor> for String {
fn from(val: &Anchor) -> Self {
val.to_string()
}
}
impl TryFrom<&str> for Anchor {
type Error = Error;
fn try_from(val: &str) -> Result<Self, Self::Error> {
Self::try_from_str(val)
}
}
impl TryFrom<String> for Anchor {
type Error = Error;
fn try_from(val: String) -> Result<Self, Self::Error> {
Self::try_from_str(val.as_str())
}
}
impl std::fmt::Display for Anchor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Newest => write!(f, "{}", Self::NEWEST),
Self::Oldest => write!(f, "{}", Self::OLDEST),
Self::FirstUnread => write!(f, "{}", Self::FIRST_UNREAD),
Self::MessageId(id) => write!(f, "{id}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_anchor_strings() {
[Anchor::Newest, Anchor::Oldest, Anchor::FirstUnread]
.into_iter()
.for_each(|anchor| {
let anchor_json = format!(r#""{anchor}""#);
assert_eq!(serde_json::to_string(&anchor).unwrap(), anchor_json);
assert_eq!(
serde_json::from_str::<Anchor>(&anchor_json).unwrap(),
anchor
);
});
}
#[test]
fn test_anchor_ints() {
(0..=u64::BITS)
.map(|r| Anchor::MessageId(((1u128 << r) - 1) as u64))
.for_each(|anchor| {
let anchor_json = anchor.to_string();
assert_eq!(serde_json::to_string(&anchor).unwrap(), anchor_json);
assert_eq!(
serde_json::from_str::<Anchor>(&anchor_json).unwrap(),
anchor
);
});
}
}