aboutsummaryrefslogtreecommitdiff
path: root/src/terminal/id.rs
blob: 29a300a51870590fb3504af1a94e1024950c4d20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use std::borrow::Borrow;
use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};

/// Opaque identity for a server-owned terminal.
///
/// During the pane-backed transition this is stored one-to-one beside panes,
/// but callers must not derive it from a pane id or layout position.
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct TerminalId(String);

static NEXT_TERMINAL_ID: AtomicU64 = AtomicU64::new(1);

impl TerminalId {
    pub fn alloc() -> Self {
        let micros = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|duration| duration.as_micros())
            .unwrap_or(0);
        let counter = NEXT_TERMINAL_ID.fetch_add(1, Ordering::Relaxed);
        Self(format!("term_{micros:x}{counter:x}"))
    }

    pub(crate) fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for TerminalId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0)
    }
}

impl Borrow<str> for TerminalId {
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    #[test]
    fn terminal_ids_support_borrowed_lookup_without_changing_identity() {
        let id = TerminalId::alloc();
        let serialized = serde_json::to_string(&id).unwrap();
        let restored: TerminalId = serde_json::from_str(&serialized).unwrap();
        let mut terminals = HashMap::from([(id.clone(), 1)]);

        assert_eq!(terminals.get(restored.as_str()), Some(&1));
        assert_eq!(terminals.get("missing-terminal"), None);
        *terminals.get_mut(restored.as_str()).unwrap() = 2;
        assert_eq!(terminals.remove(id.as_str()), Some(2));
        assert!(terminals.is_empty());
    }
}