Skip to main content

oxihuman_core/
action_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// A mapping from string action names to callbacks represented as closures.
6///
7/// `ActionMap` stores named actions that can be invoked by name,
8/// enabling decoupled input-to-action dispatch.
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct ActionMap {
12    entries: Vec<ActionEntry>,
13    enabled: bool,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct ActionEntry {
19    pub name: String,
20    pub category: String,
21    pub priority: u32,
22    pub enabled: bool,
23}
24
25impl Default for ActionMap {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31#[allow(dead_code)]
32impl ActionMap {
33    pub fn new() -> Self {
34        Self {
35            entries: Vec::new(),
36            enabled: true,
37        }
38    }
39
40    pub fn register(&mut self, name: &str, category: &str, priority: u32) -> bool {
41        if self.entries.iter().any(|e| e.name == name) {
42            return false;
43        }
44        self.entries.push(ActionEntry {
45            name: name.to_string(),
46            category: category.to_string(),
47            priority,
48            enabled: true,
49        });
50        true
51    }
52
53    pub fn unregister(&mut self, name: &str) -> bool {
54        let before = self.entries.len();
55        self.entries.retain(|e| e.name != name);
56        self.entries.len() < before
57    }
58
59    pub fn get(&self, name: &str) -> Option<&ActionEntry> {
60        self.entries.iter().find(|e| e.name == name)
61    }
62
63    pub fn contains(&self, name: &str) -> bool {
64        self.entries.iter().any(|e| e.name == name)
65    }
66
67    pub fn set_enabled(&mut self, name: &str, enabled: bool) -> bool {
68        if let Some(entry) = self.entries.iter_mut().find(|e| e.name == name) {
69            entry.enabled = enabled;
70            true
71        } else {
72            false
73        }
74    }
75
76    pub fn is_enabled(&self, name: &str) -> bool {
77        self.entries
78            .iter()
79            .find(|e| e.name == name)
80            .is_some_and(|e| e.enabled)
81    }
82
83    pub fn count(&self) -> usize {
84        self.entries.len()
85    }
86
87    pub fn by_category(&self, category: &str) -> Vec<&ActionEntry> {
88        self.entries
89            .iter()
90            .filter(|e| e.category == category)
91            .collect()
92    }
93
94    pub fn by_priority(&self) -> Vec<&ActionEntry> {
95        let mut sorted: Vec<&ActionEntry> = self.entries.iter().collect();
96        sorted.sort_by(|a, b| b.priority.cmp(&a.priority));
97        sorted
98    }
99
100    pub fn categories(&self) -> Vec<String> {
101        let mut cats: Vec<String> = self.entries.iter().map(|e| e.category.clone()).collect();
102        cats.sort();
103        cats.dedup();
104        cats
105    }
106
107    pub fn enabled_count(&self) -> usize {
108        self.entries.iter().filter(|e| e.enabled).count()
109    }
110
111    pub fn clear(&mut self) {
112        self.entries.clear();
113    }
114
115    pub fn names(&self) -> Vec<&str> {
116        self.entries.iter().map(|e| e.name.as_str()).collect()
117    }
118
119    pub fn set_global_enabled(&mut self, enabled: bool) {
120        self.enabled = enabled;
121    }
122
123    pub fn is_global_enabled(&self) -> bool {
124        self.enabled
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_new_empty() {
134        let map = ActionMap::new();
135        assert_eq!(map.count(), 0);
136        assert!(map.is_global_enabled());
137    }
138
139    #[test]
140    fn test_register_and_get() {
141        let mut map = ActionMap::new();
142        assert!(map.register("jump", "movement", 10));
143        assert!(map.contains("jump"));
144        let entry = map.get("jump").expect("should succeed");
145        assert_eq!(entry.category, "movement");
146        assert_eq!(entry.priority, 10);
147    }
148
149    #[test]
150    fn test_duplicate_register() {
151        let mut map = ActionMap::new();
152        assert!(map.register("fire", "combat", 5));
153        assert!(!map.register("fire", "combat", 5));
154    }
155
156    #[test]
157    fn test_unregister() {
158        let mut map = ActionMap::new();
159        map.register("run", "movement", 1);
160        assert!(map.unregister("run"));
161        assert!(!map.contains("run"));
162        assert!(!map.unregister("run"));
163    }
164
165    #[test]
166    fn test_enable_disable() {
167        let mut map = ActionMap::new();
168        map.register("crouch", "movement", 2);
169        assert!(map.is_enabled("crouch"));
170        map.set_enabled("crouch", false);
171        assert!(!map.is_enabled("crouch"));
172    }
173
174    #[test]
175    fn test_by_category() {
176        let mut map = ActionMap::new();
177        map.register("jump", "movement", 10);
178        map.register("fire", "combat", 5);
179        map.register("run", "movement", 8);
180        assert_eq!(map.by_category("movement").len(), 2);
181        assert_eq!(map.by_category("combat").len(), 1);
182    }
183
184    #[test]
185    fn test_by_priority() {
186        let mut map = ActionMap::new();
187        map.register("a", "cat", 1);
188        map.register("b", "cat", 10);
189        map.register("c", "cat", 5);
190        let sorted = map.by_priority();
191        assert_eq!(sorted[0].name, "b");
192        assert_eq!(sorted[2].name, "a");
193    }
194
195    #[test]
196    fn test_categories() {
197        let mut map = ActionMap::new();
198        map.register("a", "x", 1);
199        map.register("b", "y", 2);
200        map.register("c", "x", 3);
201        let cats = map.categories();
202        assert_eq!(cats.len(), 2);
203    }
204
205    #[test]
206    fn test_clear() {
207        let mut map = ActionMap::new();
208        map.register("a", "b", 1);
209        map.clear();
210        assert_eq!(map.count(), 0);
211    }
212
213    #[test]
214    fn test_enabled_count() {
215        let mut map = ActionMap::new();
216        map.register("a", "c", 1);
217        map.register("b", "c", 2);
218        map.set_enabled("b", false);
219        assert_eq!(map.enabled_count(), 1);
220    }
221}