Skip to main content

zulip_api/filter/
operand.rs

1use std::borrow::Cow;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Error;
6use crate::result::Result;
7
8/// Represents an operand used to filter searches and messages.
9#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
10#[serde(untagged)]
11pub enum Operand {
12    Integer(u64),
13    IntegerList(Vec<u64>),
14    Text(String),
15    TextList(Vec<String>),
16}
17
18impl Operand {
19    /// Creates a new [Operand].
20    pub const fn new() -> Self {
21        Self::Integer(0)
22    }
23
24    /// Convenience function to get the variant name's string representation.
25    pub(crate) const fn variant_name(&self) -> &'static str {
26        match self {
27            Self::Integer(_) => "Operand::Integer",
28            Self::IntegerList(_) => "Operand::IntegerList",
29            Self::Text(_) => "Operand::Text",
30            Self::TextList(_) => "Operand::TextList",
31        }
32    }
33
34    /// Attempts to get the integer variant.
35    pub fn as_integer(&self) -> Result<u64> {
36        match self {
37            Self::Integer(v) => Ok(*v),
38            o => Err(Error::invalid_field(
39                "operand",
40                o.variant_name(),
41                self.variant_name(),
42            )),
43        }
44    }
45
46    /// Creates a new integer list variant.
47    pub fn integer_list<'c, C: Into<Cow<'c, [u64]>>>(val: C) -> Self {
48        Self::IntegerList(C::into(val).into())
49    }
50
51    /// Attempts to get the integer list variant.
52    pub fn as_integer_list(&self) -> Result<&[u64]> {
53        match self {
54            Self::IntegerList(v) => Ok(v.as_slice()),
55            o => Err(Error::invalid_field(
56                "operand",
57                o.variant_name(),
58                self.variant_name(),
59            )),
60        }
61    }
62
63    /// Creates a new text variant.
64    pub fn text<'c, C: Into<Cow<'c, str>>>(val: C) -> Self {
65        Self::Text(C::into(val).into())
66    }
67
68    /// Attempts to get the text variant.
69    pub fn as_text(&self) -> Result<&str> {
70        match self {
71            Self::Text(v) => Ok(v.as_str()),
72            o => Err(Error::invalid_field(
73                "operand",
74                o.variant_name(),
75                self.variant_name(),
76            )),
77        }
78    }
79
80    /// Creates a new text list variant.
81    pub fn text_list<'c, C: Into<Cow<'c, [String]>>>(val: C) -> Self {
82        Self::TextList(C::into(val).into())
83    }
84
85    /// Attempts to get the text list variant.
86    pub fn as_text_list(&self) -> Result<&[String]> {
87        match self {
88            Self::TextList(v) => Ok(v.as_slice()),
89            o => Err(Error::invalid_field(
90                "operand",
91                o.variant_name(),
92                self.variant_name(),
93            )),
94        }
95    }
96}
97
98impl Default for Operand {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl From<u64> for Operand {
105    fn from(val: u64) -> Self {
106        Self::Integer(val)
107    }
108}
109
110impl From<Vec<u64>> for Operand {
111    fn from(val: Vec<u64>) -> Self {
112        Self::IntegerList(val)
113    }
114}
115
116impl From<&[u64]> for Operand {
117    fn from(val: &[u64]) -> Self {
118        Self::IntegerList(val.into())
119    }
120}
121
122impl<const N: usize> From<&[u64; N]> for Operand {
123    fn from(val: &[u64; N]) -> Self {
124        Self::IntegerList(val.into())
125    }
126}
127
128impl<const N: usize> From<[u64; N]> for Operand {
129    fn from(val: [u64; N]) -> Self {
130        Self::IntegerList(val.into_iter().collect())
131    }
132}
133
134impl From<&str> for Operand {
135    fn from(val: &str) -> Self {
136        Self::Text(val.into())
137    }
138}
139
140impl From<String> for Operand {
141    fn from(val: String) -> Self {
142        Self::Text(val)
143    }
144}
145
146impl From<Vec<String>> for Operand {
147    fn from(val: Vec<String>) -> Self {
148        Self::TextList(val)
149    }
150}
151
152impl From<&[String]> for Operand {
153    fn from(val: &[String]) -> Self {
154        Self::TextList(val.into())
155    }
156}
157
158impl From<&[&str]> for Operand {
159    fn from(val: &[&str]) -> Self {
160        Self::TextList(val.iter().map(|&s| String::from(s)).collect())
161    }
162}
163
164impl<const N: usize> From<&[String; N]> for Operand {
165    fn from(val: &[String; N]) -> Self {
166        Self::TextList(val.into())
167    }
168}
169
170impl<const N: usize> From<&[&str; N]> for Operand {
171    fn from(val: &[&str; N]) -> Self {
172        Self::TextList(val.iter().map(|&s| String::from(s)).collect())
173    }
174}
175
176impl<const N: usize> From<[String; N]> for Operand {
177    fn from(val: [String; N]) -> Self {
178        Self::TextList(val.into())
179    }
180}
181
182impl<const N: usize> From<[&str; N]> for Operand {
183    fn from(val: [&str; N]) -> Self {
184        Self::TextList(val.into_iter().map(String::from).collect())
185    }
186}
187
188impl std::fmt::Display for Operand {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        serde_json::to_string(self)
191            .map_err(|_| std::fmt::Error)
192            .and_then(|s| write!(f, "{s}"))
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_operand() {
202        (0..=u64::BITS)
203            .map(|r| ((1u128 << r) - 1) as u64)
204            .enumerate()
205            .for_each(|(i, int)| {
206                let list_len = i + 1;
207                let int_list = vec![int; list_len];
208                let text = format!("{int}");
209                let text_list = vec![text.clone(); list_len];
210
211                let op_int = Operand::Integer(int);
212                let op_int_list = Operand::IntegerList(int_list.clone());
213                let op_text = Operand::Text(text.clone());
214                let op_text_list = Operand::TextList(text_list.clone());
215
216                assert_eq!(op_int.as_integer(), Ok(int));
217                assert_eq!(op_int_list.as_integer_list(), Ok(int_list.as_slice()));
218                assert_eq!(op_text.as_text(), Ok(text.as_str()));
219                assert_eq!(op_text_list.as_text_list(), Ok(text_list.as_slice()));
220
221                [op_int, op_int_list, op_text, op_text_list]
222                    .into_iter()
223                    .for_each(|op| {
224                        let op_json = format!("{op}");
225
226                        assert_eq!(serde_json::to_string(&op).unwrap(), op_json);
227                        assert_eq!(serde_json::from_str::<Operand>(&op_json).unwrap(), op);
228                    });
229            });
230    }
231}