Skip to main content

altium_format/tree/
node.rs

1//! Node identification types for tree structures.
2
3use std::fmt;
4
5/// Unique identifier for a record within a tree.
6///
7/// RecordId is a lightweight handle that refers to a record by its index
8/// in the tree's internal storage. It is only valid within the context
9/// of the tree it was created from.
10#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
11pub struct RecordId(u32);
12
13impl RecordId {
14    /// The root record ID (index 0).
15    pub const ROOT: RecordId = RecordId(0);
16
17    /// Create a new RecordId from an index.
18    #[inline]
19    pub const fn new(index: u32) -> Self {
20        RecordId(index)
21    }
22
23    /// Get the underlying index.
24    #[inline]
25    pub const fn index(self) -> u32 {
26        self.0
27    }
28
29    /// Create a RecordId from a usize index.
30    #[inline]
31    pub fn from_usize(index: usize) -> Self {
32        RecordId(index as u32)
33    }
34
35    /// Get the index as usize.
36    #[inline]
37    pub fn as_usize(self) -> usize {
38        self.0 as usize
39    }
40}
41
42impl fmt::Debug for RecordId {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        write!(f, "RecordId({})", self.0)
45    }
46}
47
48impl fmt::Display for RecordId {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        write!(f, "#{}", self.0)
51    }
52}
53
54impl From<u32> for RecordId {
55    fn from(index: u32) -> Self {
56        RecordId(index)
57    }
58}
59
60impl From<usize> for RecordId {
61    fn from(index: usize) -> Self {
62        RecordId(index as u32)
63    }
64}
65
66impl From<RecordId> for u32 {
67    fn from(id: RecordId) -> Self {
68        id.0
69    }
70}
71
72impl From<RecordId> for usize {
73    fn from(id: RecordId) -> Self {
74        id.0 as usize
75    }
76}
77
78/// Reference to a parent record.
79///
80/// This enum provides a type-safe way to represent parent references,
81/// distinguishing between root records (no parent) and records with
82/// a valid parent reference.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
84pub enum ParentRef {
85    /// This record is a root (no parent).
86    #[default]
87    Root,
88    /// This record has a parent at the given index.
89    Index(RecordId),
90}
91
92impl ParentRef {
93    /// Create a ParentRef from an owner index value.
94    ///
95    /// Negative values or values that would be invalid indexes
96    /// are treated as Root.
97    pub fn from_owner_index(index: i32) -> Self {
98        if index < 0 {
99            ParentRef::Root
100        } else {
101            ParentRef::Index(RecordId::new(index as u32))
102        }
103    }
104
105    /// Convert to an owner index value.
106    ///
107    /// Root returns -1, otherwise returns the index.
108    pub fn to_owner_index(self) -> i32 {
109        match self {
110            ParentRef::Root => -1,
111            ParentRef::Index(id) => id.0 as i32,
112        }
113    }
114
115    /// Check if this is a root reference.
116    pub fn is_root(self) -> bool {
117        matches!(self, ParentRef::Root)
118    }
119
120    /// Get the parent RecordId, if any.
121    pub fn id(self) -> Option<RecordId> {
122        match self {
123            ParentRef::Root => None,
124            ParentRef::Index(id) => Some(id),
125        }
126    }
127}
128
129impl From<i32> for ParentRef {
130    fn from(index: i32) -> Self {
131        ParentRef::from_owner_index(index)
132    }
133}
134
135impl From<Option<RecordId>> for ParentRef {
136    fn from(id: Option<RecordId>) -> Self {
137        match id {
138            Some(id) => ParentRef::Index(id),
139            None => ParentRef::Root,
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_record_id() {
150        let id = RecordId::new(42);
151        assert_eq!(id.index(), 42);
152        assert_eq!(format!("{}", id), "#42");
153        assert_eq!(format!("{:?}", id), "RecordId(42)");
154    }
155
156    #[test]
157    fn test_parent_ref() {
158        assert!(ParentRef::Root.is_root());
159        assert!(!ParentRef::Index(RecordId::new(0)).is_root());
160
161        assert_eq!(ParentRef::from_owner_index(-1), ParentRef::Root);
162        assert_eq!(
163            ParentRef::from_owner_index(5),
164            ParentRef::Index(RecordId::new(5))
165        );
166
167        assert_eq!(ParentRef::Root.to_owner_index(), -1);
168        assert_eq!(ParentRef::Index(RecordId::new(5)).to_owner_index(), 5);
169    }
170}