ricecoder_keybinds/
engine.rs

1//! Keybind engine that combines registry and profile management
2
3use crate::conflict::ConflictDetector;
4use crate::error::EngineError;
5use crate::help::KeybindHelp;
6use crate::models::{Keybind, KeyCombo};
7use crate::parser::ParserRegistry;
8use crate::persistence::KeybindPersistence;
9use crate::profile::ProfileManager;
10use crate::registry::KeybindRegistry;
11use std::path::Path;
12
13/// Main keybind engine combining registry and profile management
14pub struct KeybindEngine {
15    registry: KeybindRegistry,
16    profile_manager: ProfileManager,
17    default_keybinds: Vec<Keybind>,
18}
19
20impl KeybindEngine {
21    /// Create a new keybind engine
22    pub fn new() -> Self {
23        KeybindEngine {
24            registry: KeybindRegistry::new(),
25            profile_manager: ProfileManager::new(),
26            default_keybinds: Vec::new(),
27        }
28    }
29
30    /// Load default keybinds from a JSON file
31    pub fn load_defaults_from_file(&mut self, path: impl AsRef<Path>) -> Result<(), EngineError> {
32        let content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
33            EngineError::DefaultsLoadError(format!("Failed to read defaults file: {}", e))
34        })?;
35
36        let parser = ParserRegistry::new();
37        let keybinds = parser.parse(&content, "json").map_err(|e| {
38            EngineError::DefaultsLoadError(format!("Failed to parse defaults: {}", e))
39        })?;
40
41        self.default_keybinds = keybinds.clone();
42        Ok(())
43    }
44
45    /// Apply default keybinds and create default profile if needed
46    pub fn apply_defaults(&mut self) -> Result<(), EngineError> {
47        if self.default_keybinds.is_empty() {
48            return Err(EngineError::DefaultsLoadError(
49                "No defaults loaded".to_string(),
50            ));
51        }
52
53        // Create default profile if it doesn't exist
54        if self.profile_manager.active_profile_name().is_none() {
55            self.create_profile("default", self.default_keybinds.clone())?;
56            self.select_profile("default")?;
57        } else {
58            // Apply defaults to current registry
59            self.apply_keybinds(self.default_keybinds.clone())?;
60        }
61
62        Ok(())
63    }
64
65    /// Reset keybinds to defaults
66    pub fn reset_to_defaults(&mut self) -> Result<(), EngineError> {
67        if self.default_keybinds.is_empty() {
68            return Err(EngineError::DefaultsLoadError(
69                "No defaults available for reset".to_string(),
70            ));
71        }
72
73        // Verify all defaults have is_default flag set
74        for keybind in &self.default_keybinds {
75            if !keybind.is_default {
76                return Err(EngineError::DefaultsLoadError(
77                    "Invalid default keybind: is_default flag not set".to_string(),
78                ));
79            }
80        }
81
82        // Apply defaults to registry
83        self.apply_keybinds(self.default_keybinds.clone())?;
84
85        Ok(())
86    }
87
88    /// Get the default keybinds
89    pub fn get_defaults(&self) -> &[Keybind] {
90        &self.default_keybinds
91    }
92
93    /// Set the default keybinds (for testing purposes)
94    pub fn set_defaults(&mut self, keybinds: Vec<Keybind>) {
95        self.default_keybinds = keybinds;
96    }
97
98    /// Apply keybinds to the engine
99    pub fn apply_keybinds(&mut self, keybinds: Vec<Keybind>) -> Result<(), EngineError> {
100        self.registry.clear();
101
102        for keybind in keybinds {
103            self.registry.register(keybind)?;
104        }
105
106        Ok(())
107    }
108
109    /// Get action for a key combination
110    pub fn get_action(&self, key: &KeyCombo) -> Option<&str> {
111        self.registry.lookup_by_key(key)
112    }
113
114    /// Get keybind for an action
115    pub fn get_keybind(&self, action_id: &str) -> Option<&Keybind> {
116        self.registry.lookup_by_action(action_id)
117    }
118
119    /// Get all keybinds
120    pub fn all_keybinds(&self) -> Vec<&Keybind> {
121        self.registry.all_keybinds()
122    }
123
124    /// Get keybinds by category
125    pub fn keybinds_by_category(&self, category: &str) -> Vec<&Keybind> {
126        self.registry.keybinds_by_category(category)
127    }
128
129    /// Get all categories
130    pub fn categories(&self) -> Vec<String> {
131        self.registry.categories()
132    }
133
134    /// Create a new profile
135    pub fn create_profile(
136        &mut self,
137        name: impl Into<String>,
138        keybinds: Vec<Keybind>,
139    ) -> Result<(), EngineError> {
140        self.profile_manager.create_profile(name, keybinds)?;
141        Ok(())
142    }
143
144    /// Select a profile and apply its keybinds immediately
145    pub fn select_profile(&mut self, name: &str) -> Result<(), EngineError> {
146        self.profile_manager.select_profile(name)?;
147        
148        // Apply the selected profile's keybinds immediately
149        let profile = self.profile_manager.get_active_profile()?;
150        let keybinds = profile.keybinds.clone();
151        self.apply_keybinds(keybinds)?;
152        
153        Ok(())
154    }
155
156    /// Delete a profile
157    pub fn delete_profile(&mut self, name: &str) -> Result<(), EngineError> {
158        self.profile_manager.delete_profile(name)?;
159        Ok(())
160    }
161
162    /// Get active profile name
163    pub fn active_profile_name(&self) -> Option<&str> {
164        self.profile_manager.active_profile_name()
165    }
166
167    /// Get number of keybinds
168    pub fn keybind_count(&self) -> usize {
169        self.registry.len()
170    }
171
172    /// Check if engine has keybinds
173    pub fn has_keybinds(&self) -> bool {
174        !self.registry.is_empty()
175    }
176
177    /// Validate keybinds for conflicts
178    pub fn validate_keybinds(&self, keybinds: &[Keybind]) -> ValidationResult {
179        // Detect conflicts
180        let conflicts = ConflictDetector::detect(keybinds);
181        let is_valid = conflicts.is_empty();
182
183        // Suggest resolutions for each conflict
184        let mut resolutions = Vec::new();
185        for conflict in &conflicts {
186            let suggestions = ConflictDetector::suggest_resolution(conflict, keybinds);
187            resolutions.extend(suggestions);
188        }
189
190        ValidationResult {
191            is_valid,
192            conflicts,
193            resolutions,
194            applied_keybinds: keybinds.len(),
195        }
196    }
197
198    /// Full validation pipeline: parse → validate → apply
199    pub fn validate_and_apply_from_string(
200        &mut self,
201        content: &str,
202        format: &str,
203    ) -> Result<ValidationResult, EngineError> {
204        // Step 1: Parse configuration
205        let parser = ParserRegistry::new();
206        let keybinds = parser.parse(content, format)?;
207
208        // Step 2: Validate for conflicts
209        let validation = self.validate_keybinds(&keybinds);
210
211        // Step 3: Apply keybinds if valid
212        if validation.is_valid {
213            self.apply_keybinds(keybinds)?;
214        }
215
216        Ok(validation)
217    }
218
219    /// Full validation pipeline with persistence: parse → validate → apply → persist
220    pub fn validate_apply_and_persist_from_string(
221        &mut self,
222        content: &str,
223        format: &str,
224        profile_name: &str,
225        persistence: &dyn KeybindPersistence,
226    ) -> Result<ValidationResult, EngineError> {
227        // Step 1: Parse configuration
228        let parser = ParserRegistry::new();
229        let keybinds = parser.parse(content, format)?;
230
231        // Step 2: Validate for conflicts
232        let validation = self.validate_keybinds(&keybinds);
233
234        // Step 3: Apply keybinds if valid
235        if validation.is_valid {
236            self.apply_keybinds(keybinds.clone())?;
237
238            // Step 4: Create profile and persist
239            self.create_profile(profile_name, keybinds)?;
240            let profile = self.profile_manager.get_active_profile()?;
241            persistence.save_profile(profile)?;
242        }
243
244        Ok(validation)
245    }
246
247    /// Get help for all keybinds
248    pub fn get_help_all(&self) -> String {
249        let keybinds = self.all_keybinds();
250        KeybindHelp::display_all(&keybinds)
251    }
252
253    /// Get help for keybinds by category
254    pub fn get_help_by_category(&self, category: &str) -> String {
255        let keybinds = self.keybinds_by_category(category);
256        KeybindHelp::display_by_category(&keybinds, category)
257    }
258
259    /// Search keybinds
260    pub fn search_keybinds(&self, query: &str) -> Vec<&Keybind> {
261        let keybinds = self.all_keybinds();
262        KeybindHelp::search(&keybinds, query)
263    }
264
265    /// Get paginated keybinds
266    pub fn get_keybinds_paginated(&self, page: usize, page_size: usize) -> crate::help::Page<&Keybind> {
267        let keybinds = self.all_keybinds();
268        KeybindHelp::paginate(&keybinds, page, page_size)
269    }
270
271    /// Get a profile by name
272    pub fn get_profile(&self, name: &str) -> Option<&crate::profile::Profile> {
273        self.profile_manager.get_profile(name)
274    }
275}
276
277impl Default for KeybindEngine {
278    fn default() -> Self {
279        Self::new()
280    }
281}
282
283/// Validation result containing conflicts and resolutions
284#[derive(Debug, Clone)]
285pub struct ValidationResult {
286    pub is_valid: bool,
287    pub conflicts: Vec<crate::conflict::Conflict>,
288    pub resolutions: Vec<crate::conflict::Resolution>,
289    pub applied_keybinds: usize,
290}
291
292impl ValidationResult {
293    /// Check if validation passed (no conflicts)
294    pub fn passed(&self) -> bool {
295        self.is_valid && self.conflicts.is_empty()
296    }
297
298    /// Get conflict count
299    pub fn conflict_count(&self) -> usize {
300        self.conflicts.len()
301    }
302}
303
304/// Helper function to initialize engine with defaults from a file
305pub fn initialize_engine_with_defaults(
306    defaults_path: impl AsRef<Path>,
307) -> Result<KeybindEngine, EngineError> {
308    let mut engine = KeybindEngine::new();
309    engine.load_defaults_from_file(defaults_path)?;
310    engine.apply_defaults()?;
311    Ok(engine)
312}
313
314/// Helper function to get the default storage location for keybind profiles
315/// 
316/// Returns a FileSystemPersistence configured to use the default storage location:
317/// `projects/ricecoder/config/keybinds/`
318pub fn get_default_persistence() -> Result<crate::persistence::FileSystemPersistence, EngineError> {
319    crate::persistence::FileSystemPersistence::with_default_location()
320        .map_err(EngineError::from)
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    use std::str::FromStr;
327
328    #[test]
329    fn test_apply_keybinds() {
330        let mut engine = KeybindEngine::new();
331        let keybinds = vec![
332            Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
333            Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
334        ];
335
336        assert!(engine.apply_keybinds(keybinds).is_ok());
337        assert_eq!(engine.keybind_count(), 2);
338    }
339
340    #[test]
341    fn test_get_action() {
342        let mut engine = KeybindEngine::new();
343        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
344
345        engine.apply_keybinds(keybinds).unwrap();
346
347        let key_combo = KeyCombo::from_str("Ctrl+S").unwrap();
348        let action = engine.get_action(&key_combo);
349        assert_eq!(action, Some("editor.save"));
350    }
351
352    #[test]
353    fn test_get_keybind() {
354        let mut engine = KeybindEngine::new();
355        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
356
357        engine.apply_keybinds(keybinds).unwrap();
358
359        let keybind = engine.get_keybind("editor.save");
360        assert!(keybind.is_some());
361        assert_eq!(keybind.unwrap().key, "Ctrl+S");
362    }
363
364    #[test]
365    fn test_all_keybinds() {
366        let mut engine = KeybindEngine::new();
367        let keybinds = vec![
368            Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
369            Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
370        ];
371
372        engine.apply_keybinds(keybinds).unwrap();
373
374        let all = engine.all_keybinds();
375        assert_eq!(all.len(), 2);
376    }
377
378    #[test]
379    fn test_categories() {
380        let mut engine = KeybindEngine::new();
381        let keybinds = vec![
382            Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
383            Keybind::new("nav.next", "Tab", "navigation", "Next"),
384        ];
385
386        engine.apply_keybinds(keybinds).unwrap();
387
388        let categories = engine.categories();
389        assert_eq!(categories.len(), 2);
390    }
391
392    #[test]
393    fn test_create_profile() {
394        let mut engine = KeybindEngine::new();
395        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
396
397        assert!(engine.create_profile("default", keybinds).is_ok());
398    }
399
400    #[test]
401    fn test_select_profile_applies_keybinds() {
402        let mut engine = KeybindEngine::new();
403        let keybinds1 = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
404        let keybinds2 = vec![Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo")];
405
406        engine.create_profile("profile1", keybinds1).unwrap();
407        engine.create_profile("profile2", keybinds2).unwrap();
408
409        // Select profile2 and verify keybinds are applied
410        engine.select_profile("profile2").unwrap();
411        assert_eq!(engine.active_profile_name(), Some("profile2"));
412        
413        // Verify profile2's keybinds are active
414        let key_combo = KeyCombo::from_str("Ctrl+Z").unwrap();
415        assert_eq!(engine.get_action(&key_combo), Some("editor.undo"));
416    }
417
418    #[test]
419    fn test_delete_profile() {
420        let mut engine = KeybindEngine::new();
421        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
422
423        engine.create_profile("profile1", keybinds.clone()).unwrap();
424        engine.create_profile("profile2", keybinds).unwrap();
425
426        // Switch to profile2 before deleting profile1
427        engine.select_profile("profile2").unwrap();
428        assert!(engine.delete_profile("profile1").is_ok());
429    }
430
431    #[test]
432    fn test_keybind_count() {
433        let mut engine = KeybindEngine::new();
434        let keybinds = vec![
435            Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
436            Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
437            Keybind::new("nav.next", "Tab", "navigation", "Next"),
438        ];
439
440        engine.apply_keybinds(keybinds).unwrap();
441        assert_eq!(engine.keybind_count(), 3);
442    }
443
444    #[test]
445    fn test_has_keybinds() {
446        let mut engine = KeybindEngine::new();
447        assert!(!engine.has_keybinds());
448
449        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
450        engine.apply_keybinds(keybinds).unwrap();
451        assert!(engine.has_keybinds());
452    }
453
454    #[test]
455    fn test_keybinds_by_category() {
456        let mut engine = KeybindEngine::new();
457        let keybinds = vec![
458            Keybind::new("editor.save", "Ctrl+S", "editing", "Save"),
459            Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo"),
460            Keybind::new("nav.next", "Tab", "navigation", "Next"),
461        ];
462
463        engine.apply_keybinds(keybinds).unwrap();
464
465        let editing = engine.keybinds_by_category("editing");
466        assert_eq!(editing.len(), 2);
467
468        let navigation = engine.keybinds_by_category("navigation");
469        assert_eq!(navigation.len(), 1);
470    }
471
472    #[test]
473    fn test_apply_keybinds_clears_previous() {
474        let mut engine = KeybindEngine::new();
475        let keybinds1 = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
476        let keybinds2 = vec![Keybind::new("editor.undo", "Ctrl+Z", "editing", "Undo")];
477
478        engine.apply_keybinds(keybinds1).unwrap();
479        assert_eq!(engine.keybind_count(), 1);
480
481        engine.apply_keybinds(keybinds2).unwrap();
482        assert_eq!(engine.keybind_count(), 1);
483
484        // Verify old keybind is gone
485        let key_combo = KeyCombo::from_str("Ctrl+S").unwrap();
486        assert_eq!(engine.get_action(&key_combo), None);
487
488        // Verify new keybind is present
489        let key_combo = KeyCombo::from_str("Ctrl+Z").unwrap();
490        assert_eq!(engine.get_action(&key_combo), Some("editor.undo"));
491    }
492
493    #[test]
494    fn test_get_keybind_returns_none_for_missing_action() {
495        let engine = KeybindEngine::new();
496        assert_eq!(engine.get_keybind("nonexistent.action"), None);
497    }
498
499    #[test]
500    fn test_get_action_returns_none_for_missing_key() {
501        let engine = KeybindEngine::new();
502        let key_combo = KeyCombo::from_str("Ctrl+X").unwrap();
503        assert_eq!(engine.get_action(&key_combo), None);
504    }
505
506    #[test]
507    fn test_active_profile_name() {
508        let mut engine = KeybindEngine::new();
509        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
510
511        engine.create_profile("default", keybinds).unwrap();
512        assert_eq!(engine.active_profile_name(), Some("default"));
513    }
514
515    #[test]
516    fn test_load_defaults_from_file() {
517        let mut engine = KeybindEngine::new();
518        
519        // Try multiple possible paths
520        let possible_paths = vec![
521            "../../../../config/keybinds/defaults.json",
522            "projects/ricecoder/config/keybinds/defaults.json",
523            "config/keybinds/defaults.json",
524        ];
525
526        let mut loaded = false;
527        for path in possible_paths {
528            if engine.load_defaults_from_file(path).is_ok() {
529                loaded = true;
530                break;
531            }
532        }
533
534        if loaded {
535            assert!(!engine.get_defaults().is_empty());
536
537            // Verify all defaults have is_default flag
538            for keybind in engine.get_defaults() {
539                assert!(keybind.is_default);
540            }
541        }
542    }
543
544    #[test]
545    fn test_apply_defaults() {
546        let mut engine = KeybindEngine::new();
547        
548        // Try multiple possible paths
549        let possible_paths = vec![
550            "../../../../config/keybinds/defaults.json",
551            "projects/ricecoder/config/keybinds/defaults.json",
552            "config/keybinds/defaults.json",
553        ];
554
555        let mut loaded = false;
556        for path in possible_paths {
557            if engine.load_defaults_from_file(path).is_ok() {
558                loaded = true;
559                break;
560            }
561        }
562
563        if loaded {
564            assert!(engine.apply_defaults().is_ok());
565
566            // Verify default profile was created
567            assert_eq!(engine.active_profile_name(), Some("default"));
568
569            // Verify keybinds were applied
570            assert!(engine.keybind_count() > 0);
571        }
572    }
573
574    #[test]
575    fn test_reset_to_defaults() {
576        let mut engine = KeybindEngine::new();
577        
578        // Try multiple possible paths
579        let possible_paths = vec![
580            "../../../../config/keybinds/defaults.json",
581            "projects/ricecoder/config/keybinds/defaults.json",
582            "config/keybinds/defaults.json",
583        ];
584
585        let mut loaded = false;
586        for path in possible_paths {
587            if engine.load_defaults_from_file(path).is_ok() {
588                loaded = true;
589                break;
590            }
591        }
592
593        if loaded {
594            engine.apply_defaults().unwrap();
595
596            let initial_count = engine.keybind_count();
597
598            // Modify keybinds
599            let custom_keybinds = vec![Keybind::new("custom.action", "Ctrl+Q", "custom", "Custom")];
600            engine.apply_keybinds(custom_keybinds).unwrap();
601            assert_eq!(engine.keybind_count(), 1);
602
603            // Reset to defaults
604            assert!(engine.reset_to_defaults().is_ok());
605            assert_eq!(engine.keybind_count(), initial_count);
606
607            // Verify defaults are restored
608            let key_combo = KeyCombo::from_str("Ctrl+S").unwrap();
609            assert_eq!(engine.get_action(&key_combo), Some("editor.save"));
610        }
611    }
612
613    #[test]
614    fn test_reset_to_defaults_without_loading() {
615        let mut engine = KeybindEngine::new();
616        assert!(engine.reset_to_defaults().is_err());
617    }
618
619    #[test]
620    fn test_apply_defaults_without_loading() {
621        let mut engine = KeybindEngine::new();
622        assert!(engine.apply_defaults().is_err());
623    }
624
625    #[test]
626    fn test_get_defaults() {
627        let mut engine = KeybindEngine::new();
628        
629        // Try multiple possible paths
630        let possible_paths = vec![
631            "../../../../config/keybinds/defaults.json",
632            "projects/ricecoder/config/keybinds/defaults.json",
633            "config/keybinds/defaults.json",
634        ];
635
636        let mut loaded = false;
637        for path in possible_paths {
638            if engine.load_defaults_from_file(path).is_ok() {
639                loaded = true;
640                break;
641            }
642        }
643
644        if loaded {
645            let defaults = engine.get_defaults();
646
647            assert!(!defaults.is_empty());
648            for keybind in defaults {
649                assert!(keybind.is_default);
650            }
651        }
652    }
653
654    #[test]
655    fn test_initialize_engine_with_defaults() {
656        // Try multiple possible paths
657        let possible_paths = vec![
658            "../../../../config/keybinds/defaults.json",
659            "projects/ricecoder/config/keybinds/defaults.json",
660            "config/keybinds/defaults.json",
661        ];
662
663        let mut engine_result = Err(EngineError::DefaultsLoadError("No path found".to_string()));
664        for path in possible_paths {
665            if let Ok(engine) = initialize_engine_with_defaults(path) {
666                engine_result = Ok(engine);
667                break;
668            }
669        }
670
671        if let Ok(engine) = engine_result {
672            assert!(engine.keybind_count() > 0);
673            assert_eq!(engine.active_profile_name(), Some("default"));
674        }
675    }
676
677    #[test]
678    fn test_get_default_persistence() {
679        use crate::Profile;
680        
681        let result = get_default_persistence();
682        assert!(result.is_ok());
683
684        let persistence = result.unwrap();
685        
686        // Verify the persistence is configured with the correct directory
687        assert!(persistence.config_dir().exists());
688        
689        // Verify we can use it to save and load profiles
690        let keybinds = vec![Keybind::new("editor.save", "Ctrl+S", "editing", "Save")];
691        let profile = Profile::new("test_default_persistence", keybinds);
692
693        assert!(persistence.save_profile(&profile).is_ok());
694        assert!(persistence.load_profile("test_default_persistence").is_ok());
695
696        // Clean up
697        let _ = persistence.delete_profile("test_default_persistence");
698    }
699}