Skip to main content

graphmind/graph/
types.rs

1//! # Core Type Definitions -- Newtype Wrappers for Type Safety
2//!
3//! This module defines the fundamental identifier and label types used throughout
4//! the graph engine. Every type here follows Rust's **newtype pattern**: wrapping
5//! a primitive (`u64` for identifiers, `String` for names) in a single-field
6//! tuple struct.
7//!
8//! ## The newtype pattern
9//!
10//! Without newtypes, a function signature like `fn create_edge(u64, u64, u64)`
11//! is ambiguous -- which argument is the edge ID, which is the source, which is
12//! the target? Newtypes make the compiler reject `create_edge(edge_id, target, source)`
13//! when the signature expects `(EdgeId, NodeId, NodeId)`. This is a **zero-cost
14//! abstraction**: the Rust compiler erases the wrapper at compile time, generating
15//! the same machine code as if you had used raw `u64` values directly.
16//!
17//! ## `Display` vs `Debug`
18//!
19//! Each type implements both traits, serving different audiences:
20//! - [`Display`](std::fmt::Display) (`{}`) produces user-facing output
21//!   (e.g., `"NodeId(42)"`), used in error messages and query results.
22//! - [`Debug`](std::fmt::Debug) (`{:?}`) produces programmer-facing output
23//!   with full structural detail, used in logs, test failures, and `dbg!()`.
24//!
25//! The `#[derive(Debug)]` attribute auto-generates `Debug`; we implement
26//! `Display` manually to control the format.
27//!
28//! ## Derived traits
29//!
30//! All types derive `Clone`, `Copy` (identifiers are trivially copyable since
31//! they are just `u64`), `PartialEq`, `Eq`, `Hash` (for use as `HashMap` keys),
32//! `PartialOrd`, `Ord` (for sorted adjacency lists and `BTreeMap` usage),
33//! and `Serialize`/`Deserialize` (for persistence and network transport).
34
35use serde::{Deserialize, Serialize};
36use std::fmt;
37
38/// Unique identifier for a node
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
40pub struct NodeId(pub u64);
41
42impl NodeId {
43    pub fn new(id: u64) -> Self {
44        NodeId(id)
45    }
46
47    pub fn as_u64(&self) -> u64 {
48        self.0
49    }
50}
51
52impl fmt::Display for NodeId {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        write!(f, "NodeId({})", self.0)
55    }
56}
57
58impl From<u64> for NodeId {
59    fn from(id: u64) -> Self {
60        NodeId(id)
61    }
62}
63
64/// Unique identifier for an edge
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
66pub struct EdgeId(pub u64);
67
68impl EdgeId {
69    pub fn new(id: u64) -> Self {
70        EdgeId(id)
71    }
72
73    pub fn as_u64(&self) -> u64 {
74        self.0
75    }
76}
77
78impl fmt::Display for EdgeId {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "EdgeId({})", self.0)
81    }
82}
83
84impl From<u64> for EdgeId {
85    fn from(id: u64) -> Self {
86        EdgeId(id)
87    }
88}
89
90/// Node label (e.g., "Person", "Employee")
91/// Implements REQ-GRAPH-002: Nodes with labels
92#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
93pub struct Label(String);
94
95impl Label {
96    pub fn new(label: impl Into<String>) -> Self {
97        Label(label.into())
98    }
99
100    pub fn as_str(&self) -> &str {
101        &self.0
102    }
103}
104
105impl fmt::Display for Label {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        write!(f, "{}", self.0)
108    }
109}
110
111impl From<String> for Label {
112    fn from(s: String) -> Self {
113        Label(s)
114    }
115}
116
117impl From<&str> for Label {
118    fn from(s: &str) -> Self {
119        Label(s.to_string())
120    }
121}
122
123/// Edge type (relationship type, e.g., "KNOWS", "WORKS_AT")
124/// Implements REQ-GRAPH-003: Edges with types
125#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
126pub struct EdgeType(String);
127
128impl EdgeType {
129    pub fn new(edge_type: impl Into<String>) -> Self {
130        EdgeType(edge_type.into())
131    }
132
133    pub fn as_str(&self) -> &str {
134        &self.0
135    }
136}
137
138impl fmt::Display for EdgeType {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(f, "{}", self.0)
141    }
142}
143
144impl From<String> for EdgeType {
145    fn from(s: String) -> Self {
146        EdgeType(s)
147    }
148}
149
150impl From<&str> for EdgeType {
151    fn from(s: &str) -> Self {
152        EdgeType(s.to_string())
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn test_node_id() {
162        let id = NodeId::new(42);
163        assert_eq!(id.as_u64(), 42);
164        assert_eq!(format!("{}", id), "NodeId(42)");
165
166        let id2: NodeId = 100.into();
167        assert_eq!(id2.as_u64(), 100);
168    }
169
170    #[test]
171    fn test_edge_id() {
172        let id = EdgeId::new(99);
173        assert_eq!(id.as_u64(), 99);
174        assert_eq!(format!("{}", id), "EdgeId(99)");
175    }
176
177    #[test]
178    fn test_label() {
179        let label = Label::new("Person");
180        assert_eq!(label.as_str(), "Person");
181        assert_eq!(format!("{}", label), "Person");
182
183        let label2: Label = "Employee".into();
184        assert_eq!(label2.as_str(), "Employee");
185    }
186
187    #[test]
188    fn test_edge_type() {
189        let edge_type = EdgeType::new("KNOWS");
190        assert_eq!(edge_type.as_str(), "KNOWS");
191        assert_eq!(format!("{}", edge_type), "KNOWS");
192    }
193
194    #[test]
195    fn test_id_ordering() {
196        let id1 = NodeId::new(1);
197        let id2 = NodeId::new(2);
198        assert!(id1 < id2);
199    }
200}