Skip to main content

zulip_api/filter/
anchor.rs

1use serde::{Deserialize, Serialize};
2
3use crate::error::Error;
4
5/// Represents a filter anchor to provide a baseline for searching.
6#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
7#[serde(rename_all = "snake_case")]
8pub enum Anchor {
9    /// Filter relative to the newest message.
10    Newest,
11    /// Filter relative to the oldest message.
12    Oldest,
13    /// Filter relative to the first unread message.
14    FirstUnread,
15    /// Filter relative to the provided message ID.
16    #[serde(untagged)]
17    MessageId(u64),
18}
19
20impl Anchor {
21    /// Represents the string value of the newest [Anchor].
22    pub const NEWEST: &str = "newest";
23    /// Represents the string value of the oldest [Anchor].
24    pub const OLDEST: &str = "oldest";
25    /// Represents the string value of the first unread [Anchor].
26    pub const FIRST_UNREAD: &str = "first_unread";
27
28    /// Creates a new [Anchor].
29    pub const fn new() -> Self {
30        Self::Newest
31    }
32
33    /// Attempts to parse the provided string into an [Anchor].
34    pub fn try_from_str(val: &str) -> Result<Self, Error> {
35        match val.to_lowercase().as_str() {
36            Self::NEWEST => Ok(Self::Newest),
37            Self::OLDEST => Ok(Self::Oldest),
38            Self::FIRST_UNREAD => Ok(Self::FirstUnread),
39            s => s
40                .parse::<u64>()
41                .map(Self::MessageId)
42                .map_err(|err| Error::io_other(format!("{err}"))),
43        }
44    }
45}
46
47impl Default for Anchor {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53impl From<Anchor> for String {
54    fn from(val: Anchor) -> Self {
55        val.to_string()
56    }
57}
58
59impl From<&Anchor> for String {
60    fn from(val: &Anchor) -> Self {
61        val.to_string()
62    }
63}
64
65impl TryFrom<&str> for Anchor {
66    type Error = Error;
67
68    fn try_from(val: &str) -> Result<Self, Self::Error> {
69        Self::try_from_str(val)
70    }
71}
72
73impl TryFrom<String> for Anchor {
74    type Error = Error;
75
76    fn try_from(val: String) -> Result<Self, Self::Error> {
77        Self::try_from_str(val.as_str())
78    }
79}
80
81impl std::fmt::Display for Anchor {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            Self::Newest => write!(f, "{}", Self::NEWEST),
85            Self::Oldest => write!(f, "{}", Self::OLDEST),
86            Self::FirstUnread => write!(f, "{}", Self::FIRST_UNREAD),
87            Self::MessageId(id) => write!(f, "{id}"),
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_anchor_strings() {
98        [Anchor::Newest, Anchor::Oldest, Anchor::FirstUnread]
99            .into_iter()
100            .for_each(|anchor| {
101                let anchor_json = format!(r#""{anchor}""#);
102
103                assert_eq!(serde_json::to_string(&anchor).unwrap(), anchor_json);
104                assert_eq!(
105                    serde_json::from_str::<Anchor>(&anchor_json).unwrap(),
106                    anchor
107                );
108            });
109    }
110
111    #[test]
112    fn test_anchor_ints() {
113        (0..=u64::BITS)
114            .map(|r| Anchor::MessageId(((1u128 << r) - 1) as u64))
115            .for_each(|anchor| {
116                let anchor_json = anchor.to_string();
117
118                assert_eq!(serde_json::to_string(&anchor).unwrap(), anchor_json);
119                assert_eq!(
120                    serde_json::from_str::<Anchor>(&anchor_json).unwrap(),
121                    anchor
122                );
123            });
124    }
125}