clu_middleware_tron/
tool.rs

1/*
2 *  @Author: José Sánchez-Gallego (gallegoj@uw.edu)
3 *  @Date: 2025-12-04
4 *  @Filename: tool.rs
5 *  @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)
6 */
7
8use std::collections::BTreeSet;
9
10/// A structure to manage command IDs and their associated UUIDs.
11/// Command IDs are allocated from a pool of 0-255.
12/// When a command is finished, its ID is returned to the pool.
13pub struct CommandID {
14    /// A HashMap mapping command IDs to UUID strings.
15    pub command_id_to_uuid: std::collections::HashMap<u16, String>,
16    /// A HashMap mapping UUID strings to commander names.
17    pub uuid_to_commander: std::collections::HashMap<String, String>,
18    /// A BTreeSet of available command IDs.
19    pub command_ids: BTreeSet<u16>,
20}
21
22impl Default for CommandID {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl CommandID {
29    /// Creates a new CommandID manager with all command IDs available.
30    pub fn new() -> Self {
31        Self {
32            command_id_to_uuid: std::collections::HashMap::new(),
33            uuid_to_commander: std::collections::HashMap::new(),
34            command_ids: (1..65535).rev().collect(),
35        }
36    }
37
38    /// Registers a command with a given UUID, commander name, and command ID.
39    pub fn register_command(&mut self, uuid: &str, commander: &str, command_id: u16) {
40        self.command_id_to_uuid.insert(command_id, uuid.to_string());
41        self.uuid_to_commander
42            .insert(uuid.to_string(), commander.to_string());
43    }
44
45    /// Retrieves and removes an available command ID from the pool.
46    pub fn get_command_id(&mut self) -> u16 {
47        let elt = self.command_ids.iter().next().cloned().unwrap();
48        self.command_ids.take(&elt).unwrap()
49    }
50
51    /// Finishes a command by its ID, removing its associations and returning the UUID.
52    /// The command ID is returned to the pool of available IDs.
53    pub fn finish_command(&mut self, command_id: u16) -> Option<String> {
54        let uuid = self.command_id_to_uuid.remove(&command_id);
55
56        if !self.command_ids.contains(&command_id) {
57            self.command_ids.insert(command_id);
58        }
59
60        if let Some(uuid) = uuid {
61            self.uuid_to_commander.remove(uuid.as_str());
62            return Some(uuid);
63        }
64
65        None
66    }
67
68    /// Retrieves the UUID associated with a given command ID, if it exists.
69    pub fn get_uuid(&self, command_id: u16) -> Option<&String> {
70        self.command_id_to_uuid.get(&command_id)
71    }
72
73    /// Checks if a command ID is currently in use.
74    pub fn is_command_id_in_use(&self, command_id: u16) -> bool {
75        self.command_id_to_uuid.contains_key(&command_id)
76    }
77
78    /// Retrieves the commander name associated with a given UUID, if it exists.
79    pub fn get_commander(&self, uuid: &str) -> Option<&String> {
80        self.uuid_to_commander.get(uuid)
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::CommandID;
87
88    #[test]
89    fn test_command_id_init() {
90        let cmd_id = CommandID::new();
91        assert_eq!(cmd_id.command_ids.len(), 65534);
92        assert_eq!(cmd_id.command_id_to_uuid.len(), 0);
93        assert_eq!(cmd_id.uuid_to_commander.len(), 0);
94    }
95
96    #[test]
97    fn test_command_id_get_command_id() {
98        let mut cmd_id = CommandID::new();
99        let id = cmd_id.get_command_id();
100        assert_eq!(id, 1);
101
102        let id2 = cmd_id.get_command_id();
103        assert_eq!(id2, 2);
104
105        cmd_id.finish_command(id);
106        let id3 = cmd_id.get_command_id();
107        assert_eq!(id3, 1);
108    }
109
110    #[test]
111    fn test_command_id_register() {
112        let uuid = "123e4567-e89b-12d3-a456-426614174000";
113        let mut cmd_id = CommandID::new();
114        let id = cmd_id.get_command_id();
115        cmd_id.register_command(uuid, "commander1", id);
116
117        assert!(cmd_id.is_command_id_in_use(id));
118        assert_eq!(cmd_id.get_uuid(id).unwrap(), uuid);
119        assert_eq!(cmd_id.command_id_to_uuid.len(), 1);
120        assert_eq!(cmd_id.uuid_to_commander.len(), 1);
121        assert_eq!(cmd_id.is_command_id_in_use(id), true);
122        assert_eq!(cmd_id.get_commander(uuid).unwrap(), "commander1");
123    }
124
125    #[test]
126    fn test_command_id_finish() {
127        let uuid = "123e4567-e89b-12d3-a456-426614174000";
128        let mut cmd_id = CommandID::new();
129        let id = cmd_id.get_command_id();
130        cmd_id.register_command(uuid, "commander1", id);
131
132        let finished_uuid = cmd_id.finish_command(id).unwrap();
133        assert_eq!(finished_uuid, uuid);
134        assert!(!cmd_id.is_command_id_in_use(id));
135        assert!(cmd_id.get_uuid(id).is_none());
136        assert!(cmd_id.get_commander(uuid).is_none());
137    }
138}