1use crate::models::Keybind;
4
5#[derive(Debug, Clone)]
7pub struct Page<T> {
8 pub items: Vec<T>,
9 pub current_page: usize,
10 pub total_pages: usize,
11}
12
13pub struct KeybindHelp;
15
16impl KeybindHelp {
17 pub fn display_all(keybinds: &[&Keybind]) -> String {
19 let mut output = String::from("# All Keybinds\n\n");
20
21 if keybinds.is_empty() {
22 output.push_str("No keybinds configured.\n");
23 return output;
24 }
25
26 let mut categories: std::collections::HashMap<String, Vec<&Keybind>> =
28 std::collections::HashMap::new();
29
30 for keybind in keybinds {
31 categories
32 .entry(keybind.category.clone())
33 .or_default()
34 .push(keybind);
35 }
36
37 let mut sorted_categories: Vec<_> = categories.keys().collect();
39 sorted_categories.sort();
40
41 for category in sorted_categories {
42 output.push_str(&format!("## {}\n\n", category));
43
44 let mut keybinds_in_category = categories[category].clone();
45 keybinds_in_category.sort_by(|a, b| a.action_id.cmp(&b.action_id));
46
47 for keybind in keybinds_in_category {
48 output.push_str(&format!(
49 "- **{}**: `{}` - {}\n",
50 keybind.action_id, keybind.key, keybind.description
51 ));
52 }
53
54 output.push('\n');
55 }
56
57 output
58 }
59
60 pub fn display_by_category(keybinds: &[&Keybind], category: &str) -> String {
62 let filtered: Vec<&Keybind> = keybinds
63 .iter()
64 .filter(|kb| kb.category == category)
65 .copied()
66 .collect();
67
68 if filtered.is_empty() {
69 return format!("No keybinds found in category: {}\n", category);
70 }
71
72 let mut output = format!("# {} Keybinds\n\n", category);
73
74 for keybind in filtered {
75 output.push_str(&format!(
76 "- **{}**: `{}` - {}\n",
77 keybind.action_id, keybind.key, keybind.description
78 ));
79 }
80
81 output
82 }
83
84 pub fn search<'a>(keybinds: &[&'a Keybind], query: &str) -> Vec<&'a Keybind> {
86 let query_lower = query.to_lowercase();
87
88 keybinds
89 .iter()
90 .filter(|kb| {
91 kb.action_id.to_lowercase().contains(&query_lower)
92 || kb.key.to_lowercase().contains(&query_lower)
93 || kb.description.to_lowercase().contains(&query_lower)
94 })
95 .copied()
96 .collect()
97 }
98
99 pub fn paginate<'a>(keybinds: &[&'a Keybind], page: usize, page_size: usize) -> Page<&'a Keybind> {
101 if page_size == 0 {
102 return Page {
103 items: Vec::new(),
104 current_page: 0,
105 total_pages: 0,
106 };
107 }
108
109 let total_pages = keybinds.len().div_ceil(page_size);
110
111 if page == 0 || page > total_pages {
112 return Page {
113 items: Vec::new(),
114 current_page: page,
115 total_pages,
116 };
117 }
118
119 let start = (page - 1) * page_size;
120 let end = std::cmp::min(start + page_size, keybinds.len());
121
122 Page {
123 items: keybinds[start..end].to_vec(),
124 current_page: page,
125 total_pages,
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_display_all() {
136 let keybinds = vec![
137 Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
138 Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
139 Keybind::new("nav.next", "Tab", "navigation", "Next"),
140 ];
141
142 let keybind_refs: Vec<&Keybind> = keybinds.iter().collect();
143 let output = KeybindHelp::display_all(&keybind_refs);
144
145 assert!(output.contains("# All Keybinds"));
146 assert!(output.contains("## editing"));
147 assert!(output.contains("## navigation"));
148 assert!(output.contains("editor.save"));
149 }
150
151 #[test]
152 fn test_display_by_category() {
153 let keybinds = vec![
154 Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
155 Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
156 Keybind::new("nav.next", "Tab", "navigation", "Next"),
157 ];
158
159 let keybind_refs: Vec<&Keybind> = keybinds.iter().collect();
160 let output = KeybindHelp::display_by_category(&keybind_refs, "editing");
161
162 assert!(output.contains("# editing Keybinds"));
163 assert!(output.contains("editor.save"));
164 assert!(output.contains("editor.undo"));
165 assert!(!output.contains("nav.next"));
166 }
167
168 #[test]
169 fn test_search() {
170 let keybinds = vec![
171 Keybind::new("editor.save", "Ctrl+S", "editing", "Save file"),
172 Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo change"),
173 Keybind::new("nav.next", "Tab", "navigation", "Next item"),
174 ];
175
176 let keybind_refs: Vec<&Keybind> = keybinds.iter().collect();
177 let results = KeybindHelp::search(&keybind_refs, "save");
178
179 assert_eq!(results.len(), 1);
180 assert_eq!(results[0].action_id, "editor.save");
181 }
182
183 #[test]
184 fn test_search_by_key() {
185 let keybinds = vec![
186 Keybind::new("editor.save", "Ctrl+S", "editing", "Save file"),
187 Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo change"),
188 ];
189
190 let keybind_refs: Vec<&Keybind> = keybinds.iter().collect();
191 let results = KeybindHelp::search(&keybind_refs, "Ctrl+S");
192
193 assert_eq!(results.len(), 1);
194 assert_eq!(results[0].action_id, "editor.save");
195 }
196
197 #[test]
198 fn test_paginate() {
199 let keybinds = vec![
200 Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
201 Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
202 Keybind::new("nav.next", "Tab", "navigation", "Next"),
203 Keybind::new("nav.prev", "Shift+Tab", "navigation", "Previous"),
204 ];
205
206 let keybind_refs: Vec<&Keybind> = keybinds.iter().collect();
207 let page = KeybindHelp::paginate(&keybind_refs, 1, 2);
208
209 assert_eq!(page.current_page, 1);
210 assert_eq!(page.total_pages, 2);
211 assert_eq!(page.items.len(), 2);
212 }
213
214 #[test]
215 fn test_paginate_invalid_page() {
216 let keybinds = vec![
217 Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
218 Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
219 ];
220
221 let keybind_refs: Vec<&Keybind> = keybinds.iter().collect();
222 let page = KeybindHelp::paginate(&keybind_refs, 10, 2);
223
224 assert_eq!(page.items.len(), 0);
225 }
226}