1use std::collections::HashMap;
6use std::time::{SystemTime, UNIX_EPOCH};
7use crate::trajectory::graph::NodeId;
8
9#[inline]
11fn current_timestamp() -> i64 {
12 SystemTime::now()
13 .duration_since(UNIX_EPOCH)
14 .map(|d| d.as_secs() as i64)
15 .unwrap_or(0)
16}
17
18pub type BranchId = u64;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum BranchStatus {
24 Active,
26 Archived,
28 Merged,
30 Recovered,
32 Orphaned,
34}
35
36impl Default for BranchStatus {
37 fn default() -> Self {
38 Self::Active
39 }
40}
41
42#[derive(Debug, Clone)]
47pub struct ForkPoint {
48 pub node_id: NodeId,
50 pub children: Vec<BranchId>,
52 pub selected_child: Option<BranchId>,
54 pub created_at: i64,
56 pub depth: u32,
58}
59
60impl ForkPoint {
61 pub fn new(node_id: NodeId, children: Vec<BranchId>, depth: u32) -> Self {
63 Self {
64 node_id,
65 children,
66 selected_child: None,
67 created_at: current_timestamp(),
68 depth,
69 }
70 }
71
72 #[inline]
74 pub fn is_branch_point(&self) -> bool {
75 self.children.len() > 1
76 }
77
78 #[inline]
80 pub fn child_count(&self) -> usize {
81 self.children.len()
82 }
83
84 pub fn select(&mut self, branch_id: BranchId) -> Result<(), BranchError> {
86 if self.children.contains(&branch_id) {
87 self.selected_child = Some(branch_id);
88 Ok(())
89 } else {
90 Err(BranchError::InvalidBranch(branch_id))
91 }
92 }
93}
94
95#[derive(Debug, Clone)]
101pub struct Branch {
102 pub id: BranchId,
104 pub fork_point: NodeId,
106 pub head: NodeId,
108 pub status: BranchStatus,
110 pub parent_branch: Option<BranchId>,
112 pub child_branches: Vec<BranchId>,
114 pub nodes: Vec<NodeId>,
116 pub metadata: HashMap<String, String>,
118 pub created_at: i64,
120 pub updated_at: i64,
122 pub label: Option<String>,
124}
125
126impl Branch {
127 pub fn new(id: BranchId, fork_point: NodeId, head: NodeId) -> Self {
129 let now = current_timestamp();
130 Self {
131 id,
132 fork_point,
133 head,
134 status: BranchStatus::Active,
135 parent_branch: None,
136 child_branches: Vec::new(),
137 nodes: vec![head],
138 metadata: HashMap::new(),
139 created_at: now,
140 updated_at: now,
141 label: None,
142 }
143 }
144
145 pub fn root(id: BranchId, root_node: NodeId) -> Self {
147 let now = current_timestamp();
148 Self {
149 id,
150 fork_point: root_node,
151 head: root_node,
152 status: BranchStatus::Active,
153 parent_branch: None,
154 child_branches: Vec::new(),
155 nodes: vec![root_node],
156 metadata: HashMap::new(),
157 created_at: now,
158 updated_at: now,
159 label: Some("main".to_string()),
160 }
161 }
162
163 #[inline]
165 pub fn is_active(&self) -> bool {
166 self.status == BranchStatus::Active
167 }
168
169 #[inline]
171 pub fn is_root(&self) -> bool {
172 self.parent_branch.is_none()
173 }
174
175 #[inline]
177 pub fn is_merged(&self) -> bool {
178 self.status == BranchStatus::Merged
179 }
180
181 pub fn archive(&mut self) {
183 self.status = BranchStatus::Archived;
184 self.updated_at = current_timestamp();
185 }
186
187 pub fn mark_merged(&mut self) {
189 self.status = BranchStatus::Merged;
190 self.updated_at = current_timestamp();
191 }
192
193 pub fn recover(&mut self) {
195 self.status = BranchStatus::Recovered;
196 self.updated_at = current_timestamp();
197 }
198
199 pub fn add_node(&mut self, node_id: NodeId) {
201 self.nodes.push(node_id);
202 self.head = node_id;
203 self.updated_at = current_timestamp();
204 }
205
206 #[inline]
208 pub fn len(&self) -> usize {
209 self.nodes.len()
210 }
211
212 #[inline]
214 pub fn is_empty(&self) -> bool {
215 self.nodes.is_empty()
216 }
217
218 pub fn set_label(&mut self, label: impl Into<String>) {
220 self.label = Some(label.into());
221 self.updated_at = current_timestamp();
222 }
223
224 pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
226 self.metadata.insert(key.into(), value.into());
227 self.updated_at = current_timestamp();
228 }
229}
230
231#[derive(Debug, Clone)]
236pub enum BranchOperation {
237 Split {
240 from: BranchId,
242 to: BranchId,
244 at: NodeId,
246 timestamp: i64,
248 },
249 Merge {
252 from: BranchId,
254 into: BranchId,
256 at: NodeId,
258 timestamp: i64,
260 },
261 Recover {
263 branch: BranchId,
265 strategy: String,
267 timestamp: i64,
269 },
270 Traverse {
272 from: BranchId,
274 to: BranchId,
276 timestamp: i64,
278 },
279 Archive {
281 branch: BranchId,
283 reason: Option<String>,
285 timestamp: i64,
287 },
288 Fork {
290 parent: BranchId,
292 child: BranchId,
294 at: NodeId,
296 timestamp: i64,
298 },
299}
300
301impl BranchOperation {
302 pub fn split(from: BranchId, to: BranchId, at: NodeId) -> Self {
304 Self::Split {
305 from,
306 to,
307 at,
308 timestamp: current_timestamp(),
309 }
310 }
311
312 pub fn merge(from: BranchId, into: BranchId, at: NodeId) -> Self {
314 Self::Merge {
315 from,
316 into,
317 at,
318 timestamp: current_timestamp(),
319 }
320 }
321
322 pub fn recover(branch: BranchId, strategy: impl Into<String>) -> Self {
324 Self::Recover {
325 branch,
326 strategy: strategy.into(),
327 timestamp: current_timestamp(),
328 }
329 }
330
331 pub fn traverse(from: BranchId, to: BranchId) -> Self {
333 Self::Traverse {
334 from,
335 to,
336 timestamp: current_timestamp(),
337 }
338 }
339
340 pub fn archive(branch: BranchId, reason: Option<String>) -> Self {
342 Self::Archive {
343 branch,
344 reason,
345 timestamp: current_timestamp(),
346 }
347 }
348
349 pub fn fork(parent: BranchId, child: BranchId, at: NodeId) -> Self {
351 Self::Fork {
352 parent,
353 child,
354 at,
355 timestamp: current_timestamp(),
356 }
357 }
358
359 pub fn timestamp(&self) -> i64 {
361 match self {
362 Self::Split { timestamp, .. } => *timestamp,
363 Self::Merge { timestamp, .. } => *timestamp,
364 Self::Recover { timestamp, .. } => *timestamp,
365 Self::Traverse { timestamp, .. } => *timestamp,
366 Self::Archive { timestamp, .. } => *timestamp,
367 Self::Fork { timestamp, .. } => *timestamp,
368 }
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
374pub enum BranchError {
375 BranchNotFound(BranchId),
377 NodeNotFound(NodeId),
379 CycleDetected,
381 CannotSplitRoot,
383 AlreadyMerged(BranchId),
385 InvalidBranch(BranchId),
387 SelfMerge,
389 NoParent(BranchId),
391 InvalidState(String),
393}
394
395impl std::fmt::Display for BranchError {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 match self {
398 Self::BranchNotFound(id) => write!(f, "Branch {} not found", id),
399 Self::NodeNotFound(id) => write!(f, "Node {} not found", id),
400 Self::CycleDetected => write!(f, "Operation would create a cycle"),
401 Self::CannotSplitRoot => write!(f, "Cannot split the root node"),
402 Self::AlreadyMerged(id) => write!(f, "Branch {} is already merged", id),
403 Self::InvalidBranch(id) => write!(f, "Invalid branch ID: {}", id),
404 Self::SelfMerge => write!(f, "Cannot merge branch with itself"),
405 Self::NoParent(id) => write!(f, "Branch {} has no parent", id),
406 Self::InvalidState(msg) => write!(f, "Invalid state: {}", msg),
407 }
408 }
409}
410
411impl std::error::Error for BranchError {}
412
413#[cfg(test)]
414mod tests {
415 use super::*;
416
417 #[test]
418 fn test_branch_creation() {
419 let branch = Branch::new(1, 100, 101);
420 assert_eq!(branch.id, 1);
421 assert_eq!(branch.fork_point, 100);
422 assert_eq!(branch.head, 101);
423 assert!(branch.is_active());
424 assert!(branch.parent_branch.is_none());
427 }
428
429 #[test]
430 fn test_root_branch() {
431 let branch = Branch::root(0, 1);
432 assert!(branch.is_root());
433 assert_eq!(branch.label, Some("main".to_string()));
434 }
435
436 #[test]
437 fn test_branch_archive() {
438 let mut branch = Branch::new(1, 100, 101);
439 assert!(branch.is_active());
440 branch.archive();
441 assert!(!branch.is_active());
442 assert_eq!(branch.status, BranchStatus::Archived);
443 }
444
445 #[test]
446 fn test_branch_recover() {
447 let mut branch = Branch::new(1, 100, 101);
448 branch.archive();
449 branch.recover();
450 assert_eq!(branch.status, BranchStatus::Recovered);
451 }
452
453 #[test]
454 fn test_fork_point() {
455 let mut fork = ForkPoint::new(100, vec![1, 2, 3], 5);
456 assert!(fork.is_branch_point());
457 assert_eq!(fork.child_count(), 3);
458
459 assert!(fork.select(2).is_ok());
460 assert_eq!(fork.selected_child, Some(2));
461
462 assert!(fork.select(99).is_err());
463 }
464
465 #[test]
466 fn test_branch_operations() {
467 let split_op = BranchOperation::split(1, 2, 100);
468 assert!(split_op.timestamp() > 0);
469
470 let merge_op = BranchOperation::merge(2, 1, 100);
471 assert!(merge_op.timestamp() > 0);
472
473 let recover_op = BranchOperation::recover(1, "manual");
474 assert!(recover_op.timestamp() > 0);
475 }
476
477 #[test]
478 fn test_branch_add_node() {
479 let mut branch = Branch::new(1, 100, 101);
480 assert_eq!(branch.len(), 1);
481
482 branch.add_node(102);
483 assert_eq!(branch.len(), 2);
484 assert_eq!(branch.head, 102);
485 assert!(branch.nodes.contains(&102));
486 }
487
488 #[test]
489 fn test_branch_metadata() {
490 let mut branch = Branch::new(1, 100, 101);
491 branch.add_metadata("source", "regeneration");
492 branch.set_label("experiment-1");
493
494 assert_eq!(branch.metadata.get("source"), Some(&"regeneration".to_string()));
495 assert_eq!(branch.label, Some("experiment-1".to_string()));
496 }
497}