Skip to main content

noxu_tree/
map_ln.rs

1//! MapLN  -  Leaf Node that maps database IDs to database metadata.
2//!
3//!
4//! A MapLN stores the configuration and state for a database. MapLNs live
5//! in the database mapping tree (DbTree) and map database IDs to their
6//! metadata, including whether the database is deleted or transient.
7
8use crate::ln::Ln;
9
10/// A MapLN maps a database ID to its configuration and state.
11///
12/// It lives in the database mapping tree (DbTree). Each MapLN holds:
13/// - An underlying LN containing serialized database configuration
14/// - A database ID
15/// - Deleted flag (soft delete marker)
16/// - Transient flag (for temporary databases)
17#[derive(Debug, Clone)]
18pub struct MapLn {
19    /// The underlying LN holding serialized database config.
20    ln: Ln,
21    /// Database ID.
22    db_id: u64,
23    /// Whether the database is deleted.
24    deleted: bool,
25    /// Whether the database is transient (temporary).
26    is_transient: bool,
27}
28
29impl MapLn {
30    /// Creates a new MapLN for the given database ID with configuration data.
31    ///
32    /// # Arguments
33    ///
34    /// * `db_id` - The database ID
35    /// * `config_data` - Serialized database configuration
36    pub fn new(db_id: u64, config_data: Vec<u8>) -> Self {
37        MapLn {
38            ln: Ln::new(Some(config_data)),
39            db_id,
40            deleted: false,
41            is_transient: false,
42        }
43    }
44
45    /// Returns the database ID.
46    pub fn get_db_id(&self) -> u64 {
47        self.db_id
48    }
49
50    /// Returns true if the database is marked as deleted.
51    pub fn is_deleted(&self) -> bool {
52        self.deleted
53    }
54
55    /// Sets the deleted flag.
56    pub fn set_deleted(&mut self, deleted: bool) {
57        self.deleted = deleted;
58        self.ln.set_dirty();
59    }
60
61    /// Returns true if the database is transient (temporary).
62    pub fn is_transient(&self) -> bool {
63        self.is_transient
64    }
65
66    /// Sets the transient flag.
67    pub fn set_transient(&mut self, transient: bool) {
68        self.is_transient = transient;
69        self.ln.set_dirty();
70    }
71
72    /// Returns a reference to the underlying LN.
73    pub fn get_ln(&self) -> &Ln {
74        &self.ln
75    }
76
77    /// Returns a mutable reference to the underlying LN.
78    pub fn get_ln_mut(&mut self) -> &mut Ln {
79        &mut self.ln
80    }
81
82    /// Returns the serialized size of this MapLN.
83    pub fn log_size(&self) -> usize {
84        self.ln.log_size() + 8 + 1 + 1 // db_id + deleted flag + transient flag
85    }
86
87    /// Writes this MapLN to a byte buffer.
88    pub fn write_to_log(&self, buf: &mut Vec<u8>) {
89        self.ln.write_to_log(buf);
90        buf.extend_from_slice(&self.db_id.to_be_bytes());
91        buf.push(if self.deleted { 1 } else { 0 });
92        buf.push(if self.is_transient { 1 } else { 0 });
93    }
94
95    /// Reads a MapLN from a byte buffer.
96    pub fn read_from_log(buf: &[u8]) -> std::io::Result<Self> {
97        let ln = Ln::read_from_log(buf)?;
98        let ln_size = ln.log_size();
99        let remaining = &buf[ln_size..];
100
101        use byteorder::{BigEndian, ReadBytesExt};
102        use std::io::Cursor;
103        let mut cursor = Cursor::new(remaining);
104        let db_id = cursor.read_u64::<BigEndian>()?;
105        let deleted = cursor.read_u8()? != 0;
106        let is_transient = cursor.read_u8()? != 0;
107
108        Ok(MapLn { ln, db_id, deleted, is_transient })
109    }
110}
111
112impl std::fmt::Display for MapLn {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        write!(
115            f,
116            "<mapln db_id={} deleted={} transient={}>",
117            self.db_id, self.deleted, self.is_transient
118        )
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use noxu_util::Vlsn;
126
127    #[test]
128    fn test_map_ln_new() {
129        let config = b"database config data".to_vec();
130        let map_ln = MapLn::new(42, config.clone());
131
132        assert_eq!(map_ln.get_db_id(), 42);
133        assert!(!map_ln.is_deleted());
134        assert!(!map_ln.is_transient());
135        assert_eq!(map_ln.get_ln().get_data(), Some(config.as_slice()));
136    }
137
138    #[test]
139    fn test_map_ln_roundtrip() {
140        let config = b"test config".to_vec();
141        let mut map_ln = MapLn::new(100, config.clone());
142        map_ln.get_ln_mut().set_vlsn(Vlsn::new(50));
143
144        let mut buf = Vec::new();
145        map_ln.write_to_log(&mut buf);
146
147        let map_ln2 = MapLn::read_from_log(&buf).unwrap();
148
149        assert_eq!(map_ln2.get_db_id(), 100);
150        assert!(!map_ln2.is_deleted());
151        assert!(!map_ln2.is_transient());
152        assert_eq!(map_ln2.get_ln().get_data(), Some(config.as_slice()));
153        assert_eq!(map_ln2.get_ln().get_vlsn().sequence(), 50);
154    }
155
156    #[test]
157    fn test_map_ln_deleted() {
158        let config = b"config".to_vec();
159        let mut map_ln = MapLn::new(200, config);
160
161        assert!(!map_ln.is_deleted());
162
163        map_ln.set_deleted(true);
164        assert!(map_ln.is_deleted());
165        assert!(map_ln.get_ln().is_dirty());
166
167        // Round-trip with deleted flag
168        let mut buf = Vec::new();
169        map_ln.write_to_log(&mut buf);
170
171        let map_ln2 = MapLn::read_from_log(&buf).unwrap();
172
173        assert_eq!(map_ln2.get_db_id(), 200);
174        assert!(map_ln2.is_deleted());
175        assert!(!map_ln2.is_transient());
176    }
177
178    #[test]
179    fn test_map_ln_transient() {
180        let config = b"transient config".to_vec();
181        let mut map_ln = MapLn::new(300, config);
182
183        assert!(!map_ln.is_transient());
184
185        map_ln.set_transient(true);
186        assert!(map_ln.is_transient());
187        assert!(map_ln.get_ln().is_dirty());
188
189        // Round-trip with transient flag
190        let mut buf = Vec::new();
191        map_ln.write_to_log(&mut buf);
192
193        let map_ln2 = MapLn::read_from_log(&buf).unwrap();
194
195        assert_eq!(map_ln2.get_db_id(), 300);
196        assert!(!map_ln2.is_deleted());
197        assert!(map_ln2.is_transient());
198    }
199
200    #[test]
201    fn test_map_ln_both_flags() {
202        let config = b"config".to_vec();
203        let mut map_ln = MapLn::new(400, config);
204
205        map_ln.set_deleted(true);
206        map_ln.set_transient(true);
207
208        let mut buf = Vec::new();
209        map_ln.write_to_log(&mut buf);
210
211        let map_ln2 = MapLn::read_from_log(&buf).unwrap();
212
213        assert_eq!(map_ln2.get_db_id(), 400);
214        assert!(map_ln2.is_deleted());
215        assert!(map_ln2.is_transient());
216    }
217
218    #[test]
219    fn test_map_ln_log_size() {
220        let config = b"test".to_vec();
221        let map_ln = MapLn::new(500, config);
222
223        let size = map_ln.log_size();
224
225        let mut buf = Vec::new();
226        map_ln.write_to_log(&mut buf);
227
228        assert_eq!(size, buf.len());
229    }
230
231    #[test]
232    fn test_map_ln_display() {
233        let config = b"config".to_vec();
234        let map_ln = MapLn::new(600, config);
235
236        let s = format!("{}", map_ln);
237        assert!(s.contains("db_id=600"));
238        assert!(s.contains("deleted=false"));
239        assert!(s.contains("transient=false"));
240    }
241}