teksilo_widgets/tab_widget/id.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Stable per-tab identifiers.
5//!
6//! [`TabId`] is the identity of a tab as it lives, dies, reorders,
7//! and survives data-source mutations. Selection signals, close /
8//! reorder / pin callbacks, and accessibility relations are all
9//! keyed by `TabId` rather than by index — so reordering a tab via
10//! drag-drop never sends the active selection to a different tab.
11//!
12//! Apps either let the framework allocate fresh ids
13//! ([`TabId::fresh`]) or wrap their own external keys (file path
14//! hashes, document UUIDs, …) via [`TabId::from_raw`].
15
16use std::num::NonZeroU64;
17use std::sync::atomic::{AtomicU64, Ordering};
18
19/// Stable identity of a tab. Cheap to copy; persists across model
20/// reorders, rebuilds, and reorders triggered by drag-and-drop.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub struct TabId(NonZeroU64);
23
24impl TabId {
25 /// Allocate a new, never-before-seen id. Backed by a monotonic
26 /// global counter — overflow is theoretically possible after
27 /// 2^64 calls, at which point the universe has had bigger
28 /// problems.
29 pub fn fresh() -> Self {
30 static COUNTER: AtomicU64 = AtomicU64::new(1);
31 let raw = COUNTER.fetch_add(1, Ordering::Relaxed);
32 // The counter starts at 1 and only ever increments, so the
33 // value is non-zero in any practical run.
34 Self(NonZeroU64::new(raw).expect("TabId counter wrapped to zero"))
35 }
36
37 /// Wrap an externally-allocated key. Use this when the tab's
38 /// identity comes from an existing app-side store (document
39 /// UUID, file path hash, etc.) — calling [`TabId::fresh`] would
40 /// allocate a *new* id every restart, breaking session restore.
41 pub fn from_raw(value: NonZeroU64) -> Self {
42 Self(value)
43 }
44
45 /// The underlying non-zero `u64`. Useful when persisting tabs
46 /// across sessions: serialize this, restore via `from_raw`.
47 pub fn raw(self) -> NonZeroU64 {
48 self.0
49 }
50}
51
52impl std::fmt::Display for TabId {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 write!(f, "TabId({})", self.0.get())
55 }
56}