Skip to main content

slack_rs/commands/conv/
sort.rs

1//! Sorting functionality for conversations
2
3use crate::api::ApiResponse;
4
5/// Sort key for conversation list
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum SortKey {
8    Name,
9    Created,
10    NumMembers,
11}
12
13impl SortKey {
14    pub fn parse(s: &str) -> Result<Self, String> {
15        match s {
16            "name" => Ok(SortKey::Name),
17            "created" => Ok(SortKey::Created),
18            "num_members" => Ok(SortKey::NumMembers),
19            _ => Err(format!(
20                "Invalid sort key '{}'. Valid values: name, created, num_members",
21                s
22            )),
23        }
24    }
25}
26
27/// Sort direction
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
29pub enum SortDirection {
30    #[default]
31    Asc,
32    Desc,
33}
34
35impl SortDirection {
36    pub fn parse(s: &str) -> Result<Self, String> {
37        match s {
38            "asc" => Ok(SortDirection::Asc),
39            "desc" => Ok(SortDirection::Desc),
40            _ => Err(format!(
41                "Invalid sort direction '{}'. Valid values: asc, desc",
42                s
43            )),
44        }
45    }
46}
47
48/// Sort conversations by the specified key and direction
49pub fn sort_conversations(response: &mut ApiResponse, key: SortKey, direction: SortDirection) {
50    if let Some(channels) = response.data.get_mut("channels") {
51        if let Some(channels_array) = channels.as_array_mut() {
52            channels_array.sort_by(|a, b| {
53                let ordering = match key {
54                    SortKey::Name => {
55                        let a_name = a.get("name").and_then(|v| v.as_str()).unwrap_or("");
56                        let b_name = b.get("name").and_then(|v| v.as_str()).unwrap_or("");
57                        a_name.cmp(b_name)
58                    }
59                    SortKey::Created => {
60                        let a_created = a.get("created").and_then(|v| v.as_i64()).unwrap_or(0);
61                        let b_created = b.get("created").and_then(|v| v.as_i64()).unwrap_or(0);
62                        a_created.cmp(&b_created)
63                    }
64                    SortKey::NumMembers => {
65                        let a_members = a.get("num_members").and_then(|v| v.as_i64()).unwrap_or(0);
66                        let b_members = b.get("num_members").and_then(|v| v.as_i64()).unwrap_or(0);
67                        a_members.cmp(&b_members)
68                    }
69                };
70
71                match direction {
72                    SortDirection::Asc => ordering,
73                    SortDirection::Desc => ordering.reverse(),
74                }
75            });
76        }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use serde_json::json;
84    use std::collections::HashMap;
85
86    #[test]
87    fn test_sort_key_parse() {
88        assert_eq!(SortKey::parse("name").unwrap(), SortKey::Name);
89        assert_eq!(SortKey::parse("created").unwrap(), SortKey::Created);
90        assert_eq!(SortKey::parse("num_members").unwrap(), SortKey::NumMembers);
91        assert!(SortKey::parse("invalid").is_err());
92    }
93
94    #[test]
95    fn test_sort_direction_parse() {
96        assert_eq!(SortDirection::parse("asc").unwrap(), SortDirection::Asc);
97        assert_eq!(SortDirection::parse("desc").unwrap(), SortDirection::Desc);
98        assert!(SortDirection::parse("invalid").is_err());
99    }
100
101    #[test]
102    fn test_sort_conversations_by_name() {
103        let mut response = ApiResponse {
104            ok: true,
105            data: HashMap::from([(
106                "channels".to_string(),
107                json!([
108                    {"id": "C1", "name": "zebra"},
109                    {"id": "C2", "name": "alpha"},
110                    {"id": "C3", "name": "beta"},
111                ]),
112            )]),
113            error: None,
114        };
115
116        sort_conversations(&mut response, SortKey::Name, SortDirection::Asc);
117
118        let channels = response.data.get("channels").unwrap().as_array().unwrap();
119        assert_eq!(channels[0].get("name").unwrap().as_str().unwrap(), "alpha");
120        assert_eq!(channels[1].get("name").unwrap().as_str().unwrap(), "beta");
121        assert_eq!(channels[2].get("name").unwrap().as_str().unwrap(), "zebra");
122    }
123
124    #[test]
125    fn test_sort_conversations_by_name_desc() {
126        let mut response = ApiResponse {
127            ok: true,
128            data: HashMap::from([(
129                "channels".to_string(),
130                json!([
131                    {"id": "C1", "name": "alpha"},
132                    {"id": "C2", "name": "zebra"},
133                    {"id": "C3", "name": "beta"},
134                ]),
135            )]),
136            error: None,
137        };
138
139        sort_conversations(&mut response, SortKey::Name, SortDirection::Desc);
140
141        let channels = response.data.get("channels").unwrap().as_array().unwrap();
142        assert_eq!(channels[0].get("name").unwrap().as_str().unwrap(), "zebra");
143        assert_eq!(channels[1].get("name").unwrap().as_str().unwrap(), "beta");
144        assert_eq!(channels[2].get("name").unwrap().as_str().unwrap(), "alpha");
145    }
146
147    #[test]
148    fn test_sort_conversations_by_created() {
149        let mut response = ApiResponse {
150            ok: true,
151            data: HashMap::from([(
152                "channels".to_string(),
153                json!([
154                    {"id": "C1", "name": "newest", "created": 300},
155                    {"id": "C2", "name": "oldest", "created": 100},
156                    {"id": "C3", "name": "middle", "created": 200},
157                ]),
158            )]),
159            error: None,
160        };
161
162        sort_conversations(&mut response, SortKey::Created, SortDirection::Asc);
163
164        let channels = response.data.get("channels").unwrap().as_array().unwrap();
165        assert_eq!(channels[0].get("created").unwrap().as_i64().unwrap(), 100);
166        assert_eq!(channels[1].get("created").unwrap().as_i64().unwrap(), 200);
167        assert_eq!(channels[2].get("created").unwrap().as_i64().unwrap(), 300);
168    }
169
170    #[test]
171    fn test_sort_conversations_by_num_members() {
172        let mut response = ApiResponse {
173            ok: true,
174            data: HashMap::from([(
175                "channels".to_string(),
176                json!([
177                    {"id": "C1", "name": "large", "num_members": 100},
178                    {"id": "C2", "name": "small", "num_members": 10},
179                    {"id": "C3", "name": "medium", "num_members": 50},
180                ]),
181            )]),
182            error: None,
183        };
184
185        sort_conversations(&mut response, SortKey::NumMembers, SortDirection::Asc);
186
187        let channels = response.data.get("channels").unwrap().as_array().unwrap();
188        assert_eq!(
189            channels[0].get("num_members").unwrap().as_i64().unwrap(),
190            10
191        );
192        assert_eq!(
193            channels[1].get("num_members").unwrap().as_i64().unwrap(),
194            50
195        );
196        assert_eq!(
197            channels[2].get("num_members").unwrap().as_i64().unwrap(),
198            100
199        );
200    }
201
202    #[test]
203    fn test_sort_conversations_missing_fields() {
204        let mut response = ApiResponse {
205            ok: true,
206            data: HashMap::from([(
207                "channels".to_string(),
208                json!([
209                    {"id": "C1", "name": "has_members", "num_members": 50},
210                    {"id": "C2", "name": "no_members"},
211                    {"id": "C3", "name": "also_has", "num_members": 10},
212                ]),
213            )]),
214            error: None,
215        };
216
217        sort_conversations(&mut response, SortKey::NumMembers, SortDirection::Asc);
218
219        let channels = response.data.get("channels").unwrap().as_array().unwrap();
220        // Missing field treated as 0, so it should be first
221        assert_eq!(
222            channels[0].get("name").unwrap().as_str().unwrap(),
223            "no_members"
224        );
225        assert_eq!(
226            channels[1].get("num_members").unwrap().as_i64().unwrap(),
227            10
228        );
229        assert_eq!(
230            channels[2].get("num_members").unwrap().as_i64().unwrap(),
231            50
232        );
233    }
234}