1use std::collections::hash_map::DefaultHasher;
67use std::fmt::Debug;
68use std::hash::{Hash, Hasher};
69
70use serde::{Deserialize, Serialize};
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
78pub struct MapNodeId(pub u64);
79
80impl MapNodeId {
81 pub fn new(id: u64) -> Self {
82 Self(id)
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub struct MapEdgeId(pub u64);
89
90impl MapEdgeId {
91 pub fn new(id: u64) -> Self {
92 Self(id)
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
102pub enum MapError {
103 #[error("Node not found: {0:?}")]
105 NodeNotFound(MapNodeId),
106
107 #[error("Edge not found: {0:?}")]
109 EdgeNotFound(MapEdgeId),
110
111 #[error("Node ID conflict: {0:?}")]
113 NodeConflict(MapNodeId),
114
115 #[error("Edge ID conflict: {0:?}")]
117 EdgeConflict(MapEdgeId),
118
119 #[error("Invalid state transition: {from:?} -> {to:?}")]
121 InvalidStateTransition {
122 node_id: MapNodeId,
123 from: MapNodeState,
124 to: MapNodeState,
125 },
126
127 #[error("Root node already exists: {0:?}")]
129 RootAlreadyExists(MapNodeId),
130
131 #[error("Parent node not found: {0:?}")]
133 ParentNotFound(MapNodeId),
134
135 #[error("Node already closed: {0:?}")]
137 AlreadyClosed(MapNodeId),
138}
139
140pub type MapResult<T> = Result<T, MapError>;
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub enum AddResult<T> {
146 Added(T),
148 AlreadyExists(T),
150}
151
152impl<T> AddResult<T> {
153 pub fn is_added(&self) -> bool {
155 matches!(self, Self::Added(_))
156 }
157
158 pub fn is_already_exists(&self) -> bool {
160 matches!(self, Self::AlreadyExists(_))
161 }
162
163 pub fn into_inner(self) -> T {
165 match self {
166 Self::Added(v) | Self::AlreadyExists(v) => v,
167 }
168 }
169
170 pub fn as_inner(&self) -> &T {
172 match self {
173 Self::Added(v) | Self::AlreadyExists(v) => v,
174 }
175 }
176}
177
178pub trait ExplorationMap {
199 type Node: Debug + Clone;
201
202 type Edge: Debug + Clone;
204
205 type Update;
207
208 type Result;
210
211 fn apply(&mut self, update: Self::Update) -> Self::Result;
220
221 fn apply_batch(&mut self, updates: Vec<Self::Update>) -> Vec<Self::Result> {
223 updates.into_iter().map(|u| self.apply(u)).collect()
224 }
225
226 fn get(&self, id: MapNodeId) -> Option<&Self::Node>;
232
233 fn get_mut(&mut self, id: MapNodeId) -> Option<&mut Self::Node>;
235
236 fn node_count(&self) -> usize;
238
239 fn frontiers(&self) -> Vec<MapNodeId>;
248}
249
250pub trait GraphExplorationMap: ExplorationMap {
258 fn get_edge(&self, id: MapEdgeId) -> Option<&Self::Edge>;
260
261 fn outgoing_edges(&self, node: MapNodeId) -> Vec<MapEdgeId>;
263
264 fn incoming_edge(&self, node: MapNodeId) -> Option<MapEdgeId>;
266
267 fn edge_count(&self) -> usize;
269
270 fn root(&self) -> Option<MapNodeId>;
272}
273
274pub trait HierarchicalMap: ExplorationMap {
278 fn parent(&self, node: MapNodeId) -> Option<MapNodeId>;
280
281 fn children(&self, node: MapNodeId) -> Vec<MapNodeId>;
283
284 fn depth(&self, node: MapNodeId) -> u32;
286}
287
288#[derive(Debug, Clone, PartialEq, Eq)]
294pub enum MapApplyResult<N> {
295 NodeCreated { node_id: MapNodeId, data: N },
297 NodeUpdated { node_id: MapNodeId },
299 NodeCompleted { node_id: MapNodeId },
301 NodeDeadEnd { node_id: MapNodeId },
303 Failed {
305 node_id: MapNodeId,
306 can_retry: bool,
307 reason: String,
308 },
309 Ignored,
311}
312
313pub trait MapState: Clone + std::fmt::Debug {
352 fn is_expandable(&self) -> bool;
354
355 fn is_closed(&self) -> bool;
357
358 fn closed() -> Self;
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
371pub enum MapNodeState {
372 #[default]
374 Open,
375 Closed,
377}
378
379impl MapState for MapNodeState {
380 fn is_expandable(&self) -> bool {
381 matches!(self, Self::Open)
382 }
383
384 fn is_closed(&self) -> bool {
385 matches!(self, Self::Closed)
386 }
387
388 fn closed() -> Self {
389 Self::Closed
390 }
391}
392
393impl MapNodeState {
394 pub fn is_open(&self) -> bool {
396 matches!(self, Self::Open)
397 }
398
399 pub fn is_closed(&self) -> bool {
401 matches!(self, Self::Closed)
402 }
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct MapNode<N, S: MapState> {
419 pub id: MapNodeId,
421 pub data: N,
423 pub state: S,
425 pub depth: u32,
427 pub parent_edge: Option<MapEdgeId>,
429}
430
431impl<N, S: MapState> MapNode<N, S> {
432 pub fn new(id: MapNodeId, data: N, state: S) -> Self {
433 Self {
434 id,
435 data,
436 state,
437 depth: 0,
438 parent_edge: None,
439 }
440 }
441
442 pub fn with_state(mut self, state: S) -> Self {
443 self.state = state;
444 self
445 }
446
447 pub fn with_depth(mut self, depth: u32) -> Self {
448 self.depth = depth;
449 self
450 }
451
452 pub fn with_parent(mut self, edge: MapEdgeId) -> Self {
453 self.parent_edge = Some(edge);
454 self
455 }
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
466pub struct MapEdge<T> {
467 pub id: MapEdgeId,
469 pub from: MapNodeId,
471 pub to: Option<MapNodeId>,
473 pub data: T,
475 pub attempts: u32,
477 pub succeeded: bool,
479}
480
481impl<T> MapEdge<T> {
482 pub fn new(id: MapEdgeId, from: MapNodeId, data: T) -> Self {
483 Self {
484 id,
485 from,
486 to: None,
487 data,
488 attempts: 0,
489 succeeded: false,
490 }
491 }
492
493 pub fn mark_success(&mut self, to: MapNodeId) {
494 self.succeeded = true;
495 self.to = Some(to);
496 self.attempts += 1;
497 }
498
499 pub fn mark_failure(&mut self) {
500 self.succeeded = false;
501 self.attempts += 1;
502 }
503}
504
505use std::collections::{HashMap, HashSet};
510
511#[derive(Debug, Clone)]
542pub struct GraphMap<N, E, S: MapState> {
543 nodes: HashMap<MapNodeId, MapNode<N, S>>,
545 edges: HashMap<MapEdgeId, MapEdge<E>>,
547 expandables: HashSet<MapNodeId>,
549 root: Option<MapNodeId>,
551 next_node_id: u64,
553 next_edge_id: u64,
555 index: HashMap<u64, MapNodeId>,
558}
559
560impl<N, E, S: MapState> Default for GraphMap<N, E, S> {
561 fn default() -> Self {
562 Self::new()
563 }
564}
565
566impl<N, E, S: MapState> GraphMap<N, E, S> {
567 pub fn new() -> Self {
569 Self {
570 nodes: HashMap::new(),
571 edges: HashMap::new(),
572 expandables: HashSet::new(),
573 root: None,
574 next_node_id: 0,
575 next_edge_id: 0,
576 index: HashMap::new(),
577 }
578 }
579
580 fn compute_hash<K: Hash>(key: &K) -> u64 {
586 let mut hasher = DefaultHasher::new();
587 key.hash(&mut hasher);
588 hasher.finish()
589 }
590
591 pub fn index_node<K, F>(&mut self, node_id: MapNodeId, key_fn: F)
595 where
596 K: Hash,
597 F: FnOnce(&N) -> K,
598 {
599 if let Some(node) = self.nodes.get(&node_id) {
600 let key = key_fn(&node.data);
601 let hash = Self::compute_hash(&key);
602 self.index.insert(hash, node_id);
603 }
604 }
605
606 pub fn get_by_key<K: Hash>(&self, key: &K) -> Option<MapNodeId> {
610 let hash = Self::compute_hash(key);
611 self.index.get(&hash).copied()
612 }
613
614 pub fn contains_key<K: Hash>(&self, key: &K) -> bool {
616 self.get_by_key(key).is_some()
617 }
618
619 pub fn clear_index(&mut self) {
621 self.index.clear();
622 }
623
624 pub fn rebuild_index<K, F>(&mut self, key_fn: F)
628 where
629 K: Hash,
630 F: Fn(&N) -> K,
631 {
632 self.index.clear();
633 for (&node_id, node) in &self.nodes {
634 let key = key_fn(&node.data);
635 let hash = Self::compute_hash(&key);
636 self.index.insert(hash, node_id);
637 }
638 }
639
640 pub fn find_node<F>(&self, predicate: F) -> Option<MapNodeId>
648 where
649 F: Fn(&MapNode<N, S>) -> bool,
650 {
651 self.nodes.values().find(|n| predicate(n)).map(|n| n.id)
652 }
653
654 pub fn find_nodes<F>(&self, predicate: F) -> Vec<MapNodeId>
656 where
657 F: Fn(&MapNode<N, S>) -> bool,
658 {
659 self.nodes
660 .values()
661 .filter(|n| predicate(n))
662 .map(|n| n.id)
663 .collect()
664 }
665
666 pub fn find_by_data(&self, data: &N) -> Option<MapNodeId>
668 where
669 N: PartialEq,
670 {
671 self.nodes.values().find(|n| &n.data == data).map(|n| n.id)
672 }
673
674 pub fn find_by_state(&self, state: &S) -> Vec<MapNodeId>
680 where
681 S: PartialEq,
682 {
683 self.nodes
684 .values()
685 .filter(|n| &n.state == state)
686 .map(|n| n.id)
687 .collect()
688 }
689
690 pub fn find_nodes_by_state<F>(&self, predicate: F) -> Vec<MapNodeId>
694 where
695 F: Fn(&S) -> bool,
696 {
697 self.nodes
698 .values()
699 .filter(|n| predicate(&n.state))
700 .map(|n| n.id)
701 .collect()
702 }
703
704 pub fn create_root_if_absent<K, F>(
712 &mut self,
713 data: N,
714 state: S,
715 key_fn: F,
716 ) -> AddResult<MapNodeId>
717 where
718 K: Hash,
719 F: FnOnce(&N) -> K,
720 {
721 let key = key_fn(&data);
722 let hash = Self::compute_hash(&key);
723
724 if let Some(&existing) = self.index.get(&hash) {
725 return AddResult::AlreadyExists(existing);
726 }
727
728 let id = self.create_root(data, state);
729 self.index.insert(hash, id);
730 AddResult::Added(id)
731 }
732
733 pub fn add_child_if_absent<K, F>(
738 &mut self,
739 parent: MapNodeId,
740 edge_data: E,
741 node_data: N,
742 node_state: S,
743 key_fn: F,
744 ) -> MapResult<AddResult<MapNodeId>>
745 where
746 K: Hash,
747 F: FnOnce(&N) -> K,
748 {
749 let key = key_fn(&node_data);
750 let hash = Self::compute_hash(&key);
751
752 if let Some(&existing) = self.index.get(&hash) {
753 return Ok(AddResult::AlreadyExists(existing));
754 }
755
756 let parent_depth = self
758 .nodes
759 .get(&parent)
760 .map(|n| n.depth)
761 .ok_or(MapError::ParentNotFound(parent))?;
762
763 let edge_id = self.add_edge(parent, edge_data);
765 let id = self.add_node(node_data, node_state, edge_id, parent_depth + 1);
766
767 if let Some(edge) = self.edges.get_mut(&edge_id) {
769 edge.to = Some(id);
770 }
771
772 self.index.insert(hash, id);
774
775 Ok(AddResult::Added(id))
776 }
777
778 pub fn create_root(&mut self, data: N, state: S) -> MapNodeId {
780 let id = self.allocate_node_id();
781 let node = MapNode::new(id, data, state.clone());
782 self.nodes.insert(id, node);
783 if state.is_expandable() {
784 self.expandables.insert(id);
785 }
786 self.root = Some(id);
787 id
788 }
789
790 fn add_node(&mut self, data: N, state: S, parent_edge: MapEdgeId, depth: u32) -> MapNodeId {
792 let id = self.allocate_node_id();
793 let node = MapNode::new(id, data, state.clone())
794 .with_parent(parent_edge)
795 .with_depth(depth);
796 self.nodes.insert(id, node);
797 if state.is_expandable() {
798 self.expandables.insert(id);
799 }
800 id
801 }
802
803 fn add_edge(&mut self, from: MapNodeId, data: E) -> MapEdgeId {
805 let id = self.allocate_edge_id();
806 let edge = MapEdge::new(id, from, data);
807 self.edges.insert(id, edge);
808
809 id
810 }
811
812 fn allocate_node_id(&mut self) -> MapNodeId {
814 let id = MapNodeId(self.next_node_id);
815 self.next_node_id += 1;
816 id
817 }
818
819 fn allocate_edge_id(&mut self) -> MapEdgeId {
821 let id = MapEdgeId(self.next_edge_id);
822 self.next_edge_id += 1;
823 id
824 }
825
826 pub fn set_state(&mut self, id: MapNodeId, state: S) {
830 if let Some(node) = self.nodes.get_mut(&id) {
831 let was_expandable = node.state.is_expandable();
832 node.state = state;
833 let is_expandable = node.state.is_expandable();
834
835 if was_expandable && !is_expandable {
837 self.expandables.remove(&id);
838 } else if !was_expandable && is_expandable {
839 self.expandables.insert(id);
840 }
841 }
842 }
843
844 pub fn add_nodes(&mut self, nodes: Vec<(N, S)>) -> Vec<MapNodeId> {
853 nodes
854 .into_iter()
855 .map(|(data, state)| {
856 if self.root.is_none() {
857 self.create_root(data, state)
858 } else {
859 let id = self.allocate_node_id();
861 let is_expandable = state.is_expandable();
862 let node = MapNode::new(id, data, state);
863 self.nodes.insert(id, node);
864 if is_expandable {
865 self.expandables.insert(id);
866 }
867 id
868 }
869 })
870 .collect()
871 }
872
873 pub fn set_states(&mut self, ids: &[MapNodeId], state: S) -> Vec<MapNodeId> {
875 let mut changed = Vec::new();
876 for &id in ids {
877 if self.nodes.contains_key(&id) {
878 self.set_state(id, state.clone());
879 changed.push(id);
880 }
881 }
882 changed
883 }
884
885 pub fn close_nodes(&mut self, ids: &[MapNodeId]) -> Vec<MapNodeId> {
890 self.set_states(ids, S::closed())
891 }
892
893 pub fn cascade_down(&mut self, id: MapNodeId) -> Vec<MapNodeId> {
901 let mut closed = Vec::new();
902 self.cascade_down_recursive(id, &mut closed);
903 closed
904 }
905
906 fn cascade_down_recursive(&mut self, id: MapNodeId, closed: &mut Vec<MapNodeId>) {
907 if !self.nodes.contains_key(&id) {
908 return;
909 }
910
911 let children: Vec<MapNodeId> = self.children_internal(id);
913 for child in children {
914 self.cascade_down_recursive(child, closed);
915 }
916
917 self.set_state(id, S::closed());
919 closed.push(id);
920 }
921
922 pub fn children(&self, node: MapNodeId) -> Vec<MapNodeId> {
924 self.children_internal(node)
925 }
926
927 fn children_internal(&self, node: MapNodeId) -> Vec<MapNodeId> {
929 self.edges
930 .values()
931 .filter(|e| e.from == node && e.to.is_some())
932 .filter_map(|e| e.to)
933 .collect()
934 }
935
936 pub fn cascade_up_if_all_closed(&mut self, id: MapNodeId) -> Vec<MapNodeId> {
940 let mut closed = Vec::new();
941 self.cascade_up_recursive(id, &mut closed);
942 closed
943 }
944
945 fn cascade_up_recursive(&mut self, id: MapNodeId, closed: &mut Vec<MapNodeId>) {
946 let parent = self.parent_internal(id);
947 if let Some(parent_id) = parent {
948 let all_children_closed = self.children_internal(parent_id).iter().all(|&child| {
949 self.nodes
950 .get(&child)
951 .map(|n| n.state.is_closed())
952 .unwrap_or(true)
953 });
954
955 if all_children_closed {
956 self.set_state(parent_id, S::closed());
957 closed.push(parent_id);
958 self.cascade_up_recursive(parent_id, closed);
959 }
960 }
961 }
962
963 pub fn parent(&self, node: MapNodeId) -> Option<MapNodeId> {
965 self.parent_internal(node)
966 }
967
968 fn parent_internal(&self, node: MapNodeId) -> Option<MapNodeId> {
970 self.nodes
971 .get(&node)
972 .and_then(|n| n.parent_edge)
973 .and_then(|edge_id| self.edges.get(&edge_id))
974 .map(|e| e.from)
975 }
976
977 pub fn close_with_cascade_up(&mut self, id: MapNodeId) -> Vec<MapNodeId> {
982 let mut closed = Vec::new();
983
984 if self.nodes.contains_key(&id) {
985 self.set_state(id, S::closed());
986 closed.push(id);
987 closed.extend(self.cascade_up_if_all_closed(id));
988 }
989
990 closed
991 }
992
993 pub fn get_nodes(&self, ids: &[MapNodeId]) -> Vec<&MapNode<N, S>> {
1002 ids.iter().filter_map(|id| self.nodes.get(id)).collect()
1003 }
1004
1005 pub fn get_node_data(&self, ids: &[MapNodeId]) -> Vec<&N> {
1010 ids.iter()
1011 .filter_map(|id| self.nodes.get(id).map(|n| &n.data))
1012 .collect()
1013 }
1014
1015 pub fn expandables(&self) -> impl Iterator<Item = MapNodeId> + '_ {
1017 self.expandables.iter().copied()
1018 }
1019
1020 pub fn expandables_count(&self) -> usize {
1022 self.expandables.len()
1023 }
1024
1025 pub fn closed_nodes(&self) -> Vec<MapNodeId> {
1027 self.nodes
1028 .values()
1029 .filter(|n| n.state.is_closed())
1030 .map(|n| n.id)
1031 .collect()
1032 }
1033
1034 pub fn working_nodes(&self) -> Vec<MapNodeId> {
1039 self.nodes
1040 .values()
1041 .filter(|n| !n.state.is_expandable() && !n.state.is_closed())
1042 .map(|n| n.id)
1043 .collect()
1044 }
1045}
1046
1047#[derive(Debug, Clone)]
1053pub enum GraphMapUpdate<N, E, S: MapState> {
1054 AddChild {
1056 parent: MapNodeId,
1058 edge_data: E,
1060 node_data: N,
1062 node_state: S,
1064 dedup_key: String,
1066 },
1067 SetState {
1069 node_id: MapNodeId,
1071 state: S,
1073 },
1074 Noop,
1076}
1077
1078impl<N, E, S> ExplorationMap for GraphMap<N, E, S>
1083where
1084 N: Debug + Clone,
1085 E: Debug + Clone,
1086 S: MapState,
1087{
1088 type Node = MapNode<N, S>;
1089 type Edge = MapEdge<E>;
1090 type Update = GraphMapUpdate<N, E, S>;
1091 type Result = MapApplyResult<N>;
1092
1093 fn apply(&mut self, update: Self::Update) -> Self::Result {
1094 match update {
1095 GraphMapUpdate::AddChild {
1096 parent,
1097 edge_data,
1098 node_data,
1099 node_state,
1100 dedup_key,
1101 } => {
1102 if self.contains_key(&dedup_key) {
1104 return MapApplyResult::Ignored;
1105 }
1106
1107 let parent_depth = match self.nodes.get(&parent) {
1109 Some(node) => node.depth,
1110 None => {
1111 return MapApplyResult::Failed {
1112 node_id: parent,
1113 can_retry: false,
1114 reason: "Parent node not found".to_string(),
1115 }
1116 }
1117 };
1118
1119 let edge_id = self.add_edge(parent, edge_data);
1121
1122 let new_node_id =
1124 self.add_node(node_data.clone(), node_state, edge_id, parent_depth + 1);
1125
1126 if let Some(edge) = self.edges.get_mut(&edge_id) {
1128 edge.mark_success(new_node_id);
1129 }
1130
1131 self.index_node(new_node_id, |_| dedup_key);
1133
1134 MapApplyResult::NodeCreated {
1135 node_id: new_node_id,
1136 data: node_data,
1137 }
1138 }
1139
1140 GraphMapUpdate::SetState { node_id, state } => {
1141 self.set_state(node_id, state);
1142 MapApplyResult::NodeUpdated { node_id }
1143 }
1144
1145 GraphMapUpdate::Noop => MapApplyResult::Ignored,
1146 }
1147 }
1148
1149 fn get(&self, id: MapNodeId) -> Option<&Self::Node> {
1150 self.nodes.get(&id)
1151 }
1152
1153 fn get_mut(&mut self, id: MapNodeId) -> Option<&mut Self::Node> {
1154 self.nodes.get_mut(&id)
1155 }
1156
1157 fn node_count(&self) -> usize {
1158 self.nodes.len()
1159 }
1160
1161 fn frontiers(&self) -> Vec<MapNodeId> {
1162 self.expandables.iter().copied().collect()
1163 }
1164}
1165
1166impl<N, E, S> GraphExplorationMap for GraphMap<N, E, S>
1171where
1172 N: Debug + Clone,
1173 E: Debug + Clone,
1174 S: MapState,
1175{
1176 fn get_edge(&self, id: MapEdgeId) -> Option<&Self::Edge> {
1177 self.edges.get(&id)
1178 }
1179
1180 fn outgoing_edges(&self, node: MapNodeId) -> Vec<MapEdgeId> {
1181 self.edges
1182 .values()
1183 .filter(|e| e.from == node)
1184 .map(|e| e.id)
1185 .collect()
1186 }
1187
1188 fn incoming_edge(&self, node: MapNodeId) -> Option<MapEdgeId> {
1189 self.nodes.get(&node).and_then(|n| n.parent_edge)
1190 }
1191
1192 fn edge_count(&self) -> usize {
1193 self.edges.len()
1194 }
1195
1196 fn root(&self) -> Option<MapNodeId> {
1197 self.root
1198 }
1199}
1200
1201impl<N, E, S> HierarchicalMap for GraphMap<N, E, S>
1206where
1207 N: Debug + Clone,
1208 E: Debug + Clone,
1209 S: MapState,
1210{
1211 fn parent(&self, node: MapNodeId) -> Option<MapNodeId> {
1212 self.nodes
1213 .get(&node)
1214 .and_then(|n| n.parent_edge)
1215 .and_then(|edge_id| self.edges.get(&edge_id))
1216 .map(|e| e.from)
1217 }
1218
1219 fn children(&self, node: MapNodeId) -> Vec<MapNodeId> {
1220 self.edges
1221 .values()
1222 .filter(|e| e.from == node && e.to.is_some())
1223 .filter_map(|e| e.to)
1224 .collect()
1225 }
1226
1227 fn depth(&self, node: MapNodeId) -> u32 {
1228 self.nodes.get(&node).map(|n| n.depth).unwrap_or(0)
1229 }
1230}
1231
1232#[cfg(test)]
1237mod tests {
1238 use super::*;
1239
1240 #[test]
1241 fn test_map_node_state() {
1242 assert!(MapNodeState::Open.is_open());
1243 assert!(!MapNodeState::Open.is_closed());
1244 assert!(!MapNodeState::Closed.is_open());
1245 assert!(MapNodeState::Closed.is_closed());
1246 }
1247
1248 #[test]
1249 fn test_map_node_builder() {
1250 let node = MapNode::new(MapNodeId(1), "task-1", MapNodeState::Open).with_depth(2);
1252
1253 assert_eq!(node.id, MapNodeId(1));
1254 assert_eq!(node.data, "task-1");
1255 assert_eq!(node.state, MapNodeState::Open);
1256 assert_eq!(node.depth, 2);
1257 assert!(node.parent_edge.is_none()); }
1259
1260 #[test]
1261 fn test_map_node_with_parent() {
1262 let node = MapNode::new(MapNodeId(2), "child", MapNodeState::Open)
1264 .with_parent(MapEdgeId(0))
1265 .with_depth(1);
1266
1267 assert_eq!(node.depth, 1);
1268 assert_eq!(node.parent_edge, Some(MapEdgeId(0)));
1269 }
1270
1271 #[test]
1272 fn test_map_edge() {
1273 let mut edge = MapEdge::new(MapEdgeId(1), MapNodeId(0), "action-1");
1274 assert!(!edge.succeeded);
1275 assert_eq!(edge.attempts, 0);
1276
1277 edge.mark_success(MapNodeId(1));
1278 assert!(edge.succeeded);
1279 assert_eq!(edge.attempts, 1);
1280 assert_eq!(edge.to, Some(MapNodeId(1)));
1281 }
1282
1283 #[test]
1288 fn test_graph_map_create_root() {
1289 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1291 let root = map.create_root("root".to_string(), MapNodeState::Open);
1292
1293 assert_eq!(map.node_count(), 1);
1294 assert_eq!(map.frontiers().len(), 1);
1295 assert_eq!(map.root(), Some(root));
1296
1297 let node = map.get(root).unwrap();
1298 assert_eq!(node.data, "root");
1299 assert_eq!(node.state, MapNodeState::Open);
1300 assert_eq!(node.depth, 0);
1301 assert!(node.parent_edge.is_none()); }
1303
1304 #[test]
1305 fn test_graph_map_create_root_closed() {
1306 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1308 let root = map.create_root("root".to_string(), MapNodeState::Closed);
1309
1310 assert_eq!(map.node_count(), 1);
1311 assert!(map.frontiers().is_empty()); assert_eq!(map.root(), Some(root));
1313 }
1314
1315 #[test]
1324 fn test_graph_map_apply_add_child() {
1325 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1326 let root = map.create_root("root".to_string(), MapNodeState::Open);
1327
1328 let update = GraphMapUpdate::AddChild {
1330 parent: root,
1331 edge_data: "action-1".to_string(),
1332 node_data: "child-1".to_string(),
1333 node_state: MapNodeState::Open,
1334 dedup_key: "child-1".to_string(),
1335 };
1336 let result = map.apply(update);
1337
1338 match result {
1340 MapApplyResult::NodeCreated { node_id, data } => {
1341 assert_eq!(data, "child-1");
1342
1343 let node = map.get(node_id).unwrap();
1344 assert_eq!(node.depth, 1); assert!(node.parent_edge.is_some()); assert_eq!(node.state, MapNodeState::Open);
1347 }
1348 _ => panic!("Expected NodeCreated, got {:?}", result),
1349 }
1350
1351 assert_eq!(map.node_count(), 2); assert_eq!(map.edge_count(), 1); assert_eq!(map.frontiers().len(), 2);
1357
1358 let root_node = map.get(root).unwrap();
1361 assert_eq!(root_node.state, MapNodeState::Open);
1362 }
1363
1364 #[test]
1366 fn test_graph_map_add_child_parent_not_found() {
1367 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1368 let update = GraphMapUpdate::AddChild {
1371 parent: MapNodeId(999), edge_data: "action".to_string(),
1373 node_data: "child".to_string(),
1374 node_state: MapNodeState::Open,
1375 dedup_key: "child".to_string(),
1376 };
1377 let result = map.apply(update);
1378
1379 match result {
1380 MapApplyResult::Failed {
1381 node_id, reason, ..
1382 } => {
1383 assert_eq!(node_id, MapNodeId(999));
1384 assert!(reason.contains("not found"));
1385 }
1386 _ => panic!("Expected Failed, got {:?}", result),
1387 }
1388
1389 assert_eq!(map.node_count(), 0);
1391 assert_eq!(map.edge_count(), 0);
1392 }
1393
1394 #[test]
1396 fn test_graph_map_add_child_duplicate_key() {
1397 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1398 let root = map.create_root("root".to_string(), MapNodeState::Open);
1399
1400 let update1 = GraphMapUpdate::AddChild {
1402 parent: root,
1403 edge_data: "action-1".to_string(),
1404 node_data: "child".to_string(),
1405 node_state: MapNodeState::Open,
1406 dedup_key: "unique-key".to_string(),
1407 };
1408 let result1 = map.apply(update1);
1409 assert!(matches!(result1, MapApplyResult::NodeCreated { .. }));
1410
1411 let update2 = GraphMapUpdate::AddChild {
1413 parent: root,
1414 edge_data: "action-2".to_string(),
1415 node_data: "child-different-data".to_string(), node_state: MapNodeState::Open,
1417 dedup_key: "unique-key".to_string(), };
1419 let result2 = map.apply(update2);
1420 assert!(matches!(result2, MapApplyResult::Ignored));
1421
1422 assert_eq!(map.node_count(), 2);
1424 assert_eq!(map.edge_count(), 1);
1426 }
1427
1428 #[test]
1435 fn test_graph_map_apply_set_state() {
1436 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1437 let root = map.create_root("root".to_string(), MapNodeState::Open);
1438
1439 let update = GraphMapUpdate::AddChild {
1441 parent: root,
1442 edge_data: "action-1".to_string(),
1443 node_data: "child".to_string(),
1444 node_state: MapNodeState::Open,
1445 dedup_key: "child".to_string(),
1446 };
1447 let child = match map.apply(update) {
1448 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1449 _ => panic!("Expected NodeCreated"),
1450 };
1451
1452 assert_eq!(map.frontiers().len(), 2);
1454
1455 let update = GraphMapUpdate::SetState {
1457 node_id: child,
1458 state: MapNodeState::Closed,
1459 };
1460 let result = map.apply(update);
1461
1462 assert!(matches!(result, MapApplyResult::NodeUpdated { .. }));
1464
1465 let child_node = map.get(child).unwrap();
1467 assert_eq!(child_node.state, MapNodeState::Closed);
1468
1469 assert_eq!(map.frontiers().len(), 1);
1471 assert!(map.frontiers().contains(&root));
1472 assert!(!map.frontiers().contains(&child));
1473 }
1474
1475 #[test]
1477 fn test_graph_map_set_state_reopen() {
1478 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1479 let root = map.create_root("root".to_string(), MapNodeState::Closed);
1480
1481 assert!(map.frontiers().is_empty());
1483
1484 let update = GraphMapUpdate::SetState {
1486 node_id: root,
1487 state: MapNodeState::Open,
1488 };
1489 map.apply(update);
1490
1491 assert_eq!(map.frontiers().len(), 1);
1493 assert!(map.frontiers().contains(&root));
1494 }
1495
1496 #[test]
1498 fn test_graph_map_hierarchical() {
1499 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1500 let root = map.create_root("root".to_string(), MapNodeState::Open);
1501
1502 let node1 = match map.apply(GraphMapUpdate::AddChild {
1504 parent: root,
1505 edge_data: "edge-1".to_string(),
1506 node_data: "node-1".to_string(),
1507 node_state: MapNodeState::Open,
1508 dedup_key: "node-1".to_string(),
1509 }) {
1510 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1511 r => panic!("Expected NodeCreated, got {:?}", r),
1512 };
1513
1514 let node2 = match map.apply(GraphMapUpdate::AddChild {
1515 parent: node1,
1516 edge_data: "edge-2".to_string(),
1517 node_data: "node-2".to_string(),
1518 node_state: MapNodeState::Open,
1519 dedup_key: "node-2".to_string(),
1520 }) {
1521 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1522 r => panic!("Expected NodeCreated, got {:?}", r),
1523 };
1524
1525 assert_eq!(map.parent(node1), Some(root));
1527 assert_eq!(map.parent(node2), Some(node1));
1528 assert_eq!(map.parent(root), None); assert_eq!(map.children(root), vec![node1]);
1532 assert_eq!(map.children(node1), vec![node2]);
1533 assert!(map.children(node2).is_empty()); assert_eq!(map.depth(root), 0);
1537 assert_eq!(map.depth(node1), 1);
1538 assert_eq!(map.depth(node2), 2);
1539 }
1540
1541 #[test]
1546 fn test_find_node() {
1547 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1548 let root = map.create_root("root".to_string(), MapNodeState::Open);
1549
1550 let target_id = match map.apply(GraphMapUpdate::AddChild {
1552 parent: root,
1553 edge_data: "edge".to_string(),
1554 node_data: "target".to_string(),
1555 node_state: MapNodeState::Open,
1556 dedup_key: "target".to_string(),
1557 }) {
1558 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1559 r => panic!("Expected NodeCreated, got {:?}", r),
1560 };
1561
1562 let found = map.find_node(|n| n.data == "target");
1564 assert_eq!(found, Some(target_id));
1565
1566 let not_found = map.find_node(|n| n.data == "nonexistent");
1568 assert!(not_found.is_none());
1569 }
1570
1571 #[test]
1572 fn test_find_nodes() {
1573 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1574 let root = map.create_root("root".to_string(), MapNodeState::Open);
1575
1576 let id1 = match map.apply(GraphMapUpdate::AddChild {
1578 parent: root,
1579 edge_data: "edge-1".to_string(),
1580 node_data: "match-1".to_string(),
1581 node_state: MapNodeState::Open,
1582 dedup_key: "match-1".to_string(),
1583 }) {
1584 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1585 r => panic!("Expected NodeCreated, got {:?}", r),
1586 };
1587
1588 let id2 = match map.apply(GraphMapUpdate::AddChild {
1589 parent: id1,
1590 edge_data: "edge-2".to_string(),
1591 node_data: "match-2".to_string(),
1592 node_state: MapNodeState::Open,
1593 dedup_key: "match-2".to_string(),
1594 }) {
1595 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1596 r => panic!("Expected NodeCreated, got {:?}", r),
1597 };
1598
1599 let found = map.find_nodes(|n| n.data.starts_with("match"));
1601 assert_eq!(found.len(), 2);
1602 assert!(found.contains(&id1));
1603 assert!(found.contains(&id2));
1604 }
1605
1606 #[test]
1607 fn test_find_by_data() {
1608 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1609 let root = map.create_root("root".to_string(), MapNodeState::Open);
1610
1611 let found = map.find_by_data(&"root".to_string());
1612 assert_eq!(found, Some(root));
1613
1614 let not_found = map.find_by_data(&"nonexistent".to_string());
1615 assert!(not_found.is_none());
1616 }
1617
1618 #[test]
1623 fn test_index_basic() {
1624 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1625 let root = map.create_root("file:auth.rs".to_string(), MapNodeState::Open);
1626
1627 map.index_node(root, |data| data.clone());
1629
1630 let found = map.get_by_key(&"file:auth.rs".to_string());
1632 assert_eq!(found, Some(root));
1633
1634 let not_found = map.get_by_key(&"file:other.rs".to_string());
1636 assert!(not_found.is_none());
1637 }
1638
1639 #[test]
1640 fn test_contains_key() {
1641 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1642 let root = map.create_root("task-1".to_string(), MapNodeState::Open);
1643 map.index_node(root, |data| data.clone());
1644
1645 assert!(map.contains_key(&"task-1".to_string()));
1646 assert!(!map.contains_key(&"task-2".to_string()));
1647 }
1648
1649 #[test]
1650 fn test_rebuild_index() {
1651 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1652 let root = map.create_root("root".to_string(), MapNodeState::Open);
1653
1654 let child = match map.apply(GraphMapUpdate::AddChild {
1655 parent: root,
1656 edge_data: "edge".to_string(),
1657 node_data: "child".to_string(),
1658 node_state: MapNodeState::Open,
1659 dedup_key: "child".to_string(),
1660 }) {
1661 MapApplyResult::NodeCreated { node_id, .. } => node_id,
1662 r => panic!("Expected NodeCreated, got {:?}", r),
1663 };
1664
1665 map.rebuild_index(|data| data.clone());
1667
1668 assert_eq!(map.get_by_key(&"root".to_string()), Some(root));
1670 assert_eq!(map.get_by_key(&"child".to_string()), Some(child));
1671 }
1672
1673 #[test]
1678 fn test_create_root_if_absent() {
1679 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1680
1681 let result1 =
1683 map.create_root_if_absent("task-1".to_string(), MapNodeState::Open, |d| d.clone());
1684 assert!(result1.is_added());
1685 let id1 = result1.into_inner();
1686
1687 let result2 =
1689 map.create_root_if_absent("task-1".to_string(), MapNodeState::Open, |d| d.clone());
1690 assert!(result2.is_already_exists());
1691 assert_eq!(result2.into_inner(), id1);
1692
1693 let result3 =
1695 map.create_root_if_absent("task-2".to_string(), MapNodeState::Open, |d| d.clone());
1696 assert!(result3.is_added());
1697 assert_ne!(result3.into_inner(), id1);
1698 }
1699
1700 #[test]
1701 fn test_add_child_if_absent() {
1702 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1703 let root = map
1704 .create_root_if_absent("root".to_string(), MapNodeState::Open, |d| d.clone())
1705 .into_inner();
1706
1707 let result1 = map
1709 .add_child_if_absent(
1710 root,
1711 "edge-1".to_string(),
1712 "child-1".to_string(),
1713 MapNodeState::Open,
1714 |d| d.clone(),
1715 )
1716 .unwrap();
1717 assert!(result1.is_added());
1718 let child1 = result1.into_inner();
1719
1720 let result2 = map
1722 .add_child_if_absent(
1723 root,
1724 "edge-2".to_string(),
1725 "child-1".to_string(),
1726 MapNodeState::Open,
1727 |d| d.clone(),
1728 )
1729 .unwrap();
1730 assert!(result2.is_already_exists());
1731 assert_eq!(result2.into_inner(), child1);
1732
1733 let result3 = map
1735 .add_child_if_absent(
1736 root,
1737 "edge-3".to_string(),
1738 "child-2".to_string(),
1739 MapNodeState::Open,
1740 |d| d.clone(),
1741 )
1742 .unwrap();
1743 assert!(result3.is_added());
1744 }
1745
1746 #[test]
1747 fn test_add_child_if_absent_parent_not_found() {
1748 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1749
1750 let result = map.add_child_if_absent(
1752 MapNodeId(999),
1753 "edge".to_string(),
1754 "child".to_string(),
1755 MapNodeState::Open,
1756 |d| d.clone(),
1757 );
1758 assert!(result.is_err());
1759 assert!(matches!(result.unwrap_err(), MapError::ParentNotFound(_)));
1760 }
1761
1762 #[test]
1763 fn test_dedup_prevents_infinite_loop() {
1764 let mut map: GraphMap<String, String, MapNodeState> = GraphMap::new();
1766 let root = map
1767 .create_root_if_absent("search:auth".to_string(), MapNodeState::Open, |d| d.clone())
1768 .into_inner();
1769
1770 let result_a = map.add_child_if_absent(
1772 root,
1773 "grep".to_string(),
1774 "file:auth.rs".to_string(),
1775 MapNodeState::Open,
1776 |d| d.clone(),
1777 );
1778 assert!(result_a.unwrap().is_added());
1779
1780 let result_b = map.add_child_if_absent(
1782 root,
1783 "grep".to_string(),
1784 "file:auth.rs".to_string(),
1785 MapNodeState::Open,
1786 |d| d.clone(),
1787 );
1788 assert!(result_b.unwrap().is_already_exists());
1789
1790 let result_c = map.add_child_if_absent(
1792 root,
1793 "grep".to_string(),
1794 "file:user.rs".to_string(),
1795 MapNodeState::Open,
1796 |d| d.clone(),
1797 );
1798 assert!(result_c.unwrap().is_added());
1799
1800 assert_eq!(map.node_count(), 3);
1802 }
1803
1804 #[test]
1805 fn test_add_result_methods() {
1806 let added: AddResult<i32> = AddResult::Added(42);
1807 assert!(added.is_added());
1808 assert!(!added.is_already_exists());
1809 assert_eq!(*added.as_inner(), 42);
1810 assert_eq!(added.into_inner(), 42);
1811
1812 let exists: AddResult<i32> = AddResult::AlreadyExists(42);
1813 assert!(!exists.is_added());
1814 assert!(exists.is_already_exists());
1815 assert_eq!(*exists.as_inner(), 42);
1816 assert_eq!(exists.into_inner(), 42);
1817 }
1818}