4848//! ```
4949
5050use alloc:: alloc:: Global ;
51- use core:: alloc:: Allocator ;
51+ use core:: { alloc:: Allocator , ops :: Index } ;
5252
5353use super :: {
5454 DIRECTIONS , DirectedGraph , Direction , EdgeId , NodeId , Predecessors , Successors , Traverse ,
5555} ;
56- use crate :: id:: { HasId , IdSlice , IdVec } ;
56+ use crate :: id:: { HasId , Id , IdSlice , IdVec } ;
5757
5858/// Sentinel value indicating "no edge" in linked lists.
5959///
@@ -69,6 +69,7 @@ const TOMBSTONE: EdgeId = EdgeId(usize::MAX);
6969/// - Two linked list heads (outgoing and incoming edges)
7070///
7171/// The `edges` array stores the head of each linked list, indexed by [`Direction`].
72+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
7273pub struct Node < N > {
7374 /// Unique identifier for this node.
7475 id : NodeId ,
@@ -102,6 +103,7 @@ impl<N> HasId for Node<N> {
102103/// The edge participates in two separate linked lists simultaneously:
103104/// 1. The source node's outgoing edge list
104105/// 2. The target node's incoming edge list
106+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
105107pub struct Edge < E > {
106108 /// Unique identifier for this edge.
107109 id : EdgeId ,
@@ -207,6 +209,7 @@ impl<E> Edge<E> {
207209/// }
208210/// # else { unreachable!() }
209211/// ```
212+ #[ derive( Debug , Clone ) ]
210213pub struct LinkedGraph < N , E , A : Allocator = Global > {
211214 /// All nodes in the graph, indexed by [`NodeId`].
212215 nodes : IdVec < NodeId , Node < N > , A > ,
@@ -284,6 +287,49 @@ impl<N, E, A: Allocator> LinkedGraph<N, E, A> {
284287 } )
285288 }
286289
290+ /// Populates the graph with nodes derived from an existing indexed collection.
291+ ///
292+ /// For each element in `domain`, calls `data` to produce the node data and adds a
293+ /// corresponding node to the graph. The resulting [`NodeId`]s will have the same
294+ /// numeric values as the source collection's indices, enabling direct ID translation
295+ /// between the domain and the graph.
296+ ///
297+ /// # Panics
298+ ///
299+ /// Panics if the graph already contains nodes. This method is intended for initial
300+ /// population only.
301+ ///
302+ /// # Examples
303+ ///
304+ /// ```rust
305+ /// # use hashql_core::graph::LinkedGraph;
306+ /// # use hashql_core::id::{Id, IdVec};
307+ /// #
308+ /// # hashql_core::id::newtype!(struct MyId(usize is 0..=usize::MAX));
309+ /// #
310+ /// let mut items: IdVec<MyId, &str> = IdVec::new();
311+ /// items.push("first");
312+ /// items.push("second");
313+ ///
314+ /// let mut graph: LinkedGraph<&str, ()> = LinkedGraph::new();
315+ /// graph.derive(&items, |_id, &value| value);
316+ ///
317+ /// // Node 0 corresponds to items[0], etc.
318+ /// assert_eq!(graph.nodes().len(), 2);
319+ /// ```
320+ pub fn derive < I , T > ( & mut self , domain : & IdSlice < I , T > , mut data : impl FnMut ( I , & T ) -> N )
321+ where
322+ I : Id ,
323+ {
324+ assert ! ( self . nodes. is_empty( ) ) ;
325+
326+ self . nodes . raw . reserve ( domain. len ( ) ) ;
327+
328+ for ( id, item) in domain. iter_enumerated ( ) {
329+ self . add_node ( data ( id, item) ) ;
330+ }
331+ }
332+
287333 /// Returns a reference to the node with the given identifier.
288334 ///
289335 /// Returns [`None`] if no node exists with that identifier.
@@ -378,6 +424,14 @@ impl<N, E, A: Allocator> LinkedGraph<N, E, A> {
378424 self . edges . as_slice ( )
379425 }
380426
427+ /// Removes all edges from the graph while preserving nodes.
428+ pub fn clear_edges ( & mut self ) {
429+ self . edges . clear ( ) ;
430+ for node in self . nodes . iter_mut ( ) {
431+ node. edges = [ TOMBSTONE ; DIRECTIONS ] ;
432+ }
433+ }
434+
381435 /// Returns an iterator over edges incident to a node in the given direction.
382436 ///
383437 /// For [`Direction::Outgoing`], iterates edges where `node` is the source.
@@ -459,6 +513,12 @@ impl<N, E, A: Allocator> LinkedGraph<N, E, A> {
459513 pub fn outgoing_edges ( & self , node : NodeId ) -> IncidentEdges < ' _ , N , E , A > {
460514 IncidentEdges :: new ( self , Direction :: Outgoing , node)
461515 }
516+
517+ /// Removes all nodes and edges from the graph.
518+ pub fn clear ( & mut self ) {
519+ self . nodes . clear ( ) ;
520+ self . edges . clear ( ) ;
521+ }
462522}
463523
464524impl < N , E > Default for LinkedGraph < N , E > {
@@ -467,6 +527,22 @@ impl<N, E> Default for LinkedGraph<N, E> {
467527 }
468528}
469529
530+ impl < N , E , A : Allocator > Index < NodeId > for LinkedGraph < N , E , A > {
531+ type Output = Node < N > ;
532+
533+ fn index ( & self , index : NodeId ) -> & Self :: Output {
534+ & self . nodes [ index]
535+ }
536+ }
537+
538+ impl < N , E , A : Allocator > Index < EdgeId > for LinkedGraph < N , E , A > {
539+ type Output = Edge < E > ;
540+
541+ fn index ( & self , index : EdgeId ) -> & Self :: Output {
542+ & self . edges [ index]
543+ }
544+ }
545+
470546impl < N , E , A : Allocator > DirectedGraph for LinkedGraph < N , E , A > {
471547 type Edge < ' this >
472548 = & ' this Edge < E >
0 commit comments