Skip to main content

mecha10_cli_core/utils/
node_identity.rs

1//! Node identity helpers shared across the CLI and its sibling binaries.
2//!
3//! A node has two name forms: scoped (`@mecha10/teleop`) and short (`teleop`). Various
4//! callers (process tracking, log file naming, the remote-runtime supervisor) need to
5//! normalize between the two, and previously reimplemented the same stripping logic
6//! independently in several places. That drift caused a bug where node-runner's scoped
7//! keys were compared against a bare `"teleop"` string because nothing routed through a
8//! single shared normalization function. This is that function.
9
10/// Strip a `@scope/name` node identifier down to its bare short name.
11///
12/// Plain (already-short) identifiers pass through unchanged, since there's no `/` to
13/// split on.
14///
15/// # Examples
16///
17/// ```
18/// use mecha10_cli_core::utils::strip_node_scope;
19///
20/// assert_eq!(strip_node_scope("@mecha10/teleop"), "teleop");
21/// assert_eq!(strip_node_scope("teleop"), "teleop");
22/// ```
23pub fn strip_node_scope(identifier: &str) -> &str {
24    identifier.rsplit('/').next().unwrap_or(identifier)
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn strips_scope_prefix() {
33        assert_eq!(strip_node_scope("@mecha10/simulator"), "simulator");
34        assert_eq!(strip_node_scope("@mecha10/video-streamer"), "video-streamer");
35        assert_eq!(strip_node_scope("@mecha10/teleop"), "teleop");
36    }
37
38    #[test]
39    fn leaves_plain_name_untouched() {
40        assert_eq!(strip_node_scope("simulator"), "simulator");
41        assert_eq!(strip_node_scope("teleop"), "teleop");
42    }
43
44    #[test]
45    fn is_consistent_across_scoped_and_short_pairs() {
46        let pairs = [
47            ("@mecha10/object-detector", "object-detector"),
48            ("@mecha10/image-classifier", "image-classifier"),
49            ("@mecha10/llm-command", "llm-command"),
50            ("@mecha10/behavior-executor", "behavior-executor"),
51            ("@mecha10/diagnostics", "diagnostics"),
52            ("@mecha10/imu", "imu"),
53            ("@mecha10/listener", "listener"),
54            ("@mecha10/motor", "motor"),
55            ("@mecha10/speaker", "speaker"),
56            ("@mecha10/teleop", "teleop"),
57            ("@mecha10/websocket-bridge", "websocket-bridge"),
58            ("teleop", "teleop"),
59            ("simulator", "simulator"),
60        ];
61
62        for (scoped, short) in pairs {
63            assert_eq!(
64                strip_node_scope(scoped),
65                short,
66                "scoped form should strip to short form"
67            );
68            assert_eq!(
69                strip_node_scope(short),
70                short,
71                "short form should pass through unchanged"
72            );
73        }
74    }
75}