forgekit_core/cfg/
paths.rs1use crate::storage::UnifiedGraphStore;
6use crate::types::{BlockId, PathId, PathKind, SymbolId};
7use std::sync::Arc;
8
9use super::load_test_cfg;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct Path {
17 pub id: PathId,
18 pub kind: PathKind,
19 pub blocks: Vec<BlockId>,
20 pub length: usize,
21}
22
23impl Path {
24 pub fn new(blocks: Vec<BlockId>) -> Self {
25 let length = blocks.len();
26 let mut hasher = blake3::Hasher::new();
27 for block in &blocks {
28 hasher.update(&block.0.to_le_bytes());
29 }
30 let hash = hasher.finalize();
31 let mut id = [0u8; 16];
32 id.copy_from_slice(&hash.as_bytes()[0..16]);
33
34 Self {
35 id: PathId(id),
36 kind: PathKind::Normal,
37 blocks,
38 length,
39 }
40 }
41
42 pub fn with_kind(blocks: Vec<BlockId>, kind: PathKind) -> Self {
43 let length = blocks.len();
44 let mut hasher = blake3::Hasher::new();
45 for block in &blocks {
46 hasher.update(&block.0.to_le_bytes());
47 }
48 let hash = hasher.finalize();
49 let mut id = [0u8; 16];
50 id.copy_from_slice(&hash.as_bytes()[0..16]);
51
52 Self {
53 id: PathId(id),
54 kind,
55 blocks,
56 length,
57 }
58 }
59
60 pub fn is_normal(&self) -> bool {
61 self.kind == PathKind::Normal
62 }
63
64 pub fn is_error(&self) -> bool {
65 self.kind == PathKind::Error
66 }
67
68 pub fn contains(&self, block: BlockId) -> bool {
69 self.blocks.contains(&block)
70 }
71
72 pub fn entry(&self) -> Option<BlockId> {
73 self.blocks.first().copied()
74 }
75
76 pub fn exit(&self) -> Option<BlockId> {
77 self.blocks.last().copied()
78 }
79}
80
81#[derive(Clone, Default)]
86pub struct PathBuilder {
87 pub(super) function: Option<SymbolId>,
88 pub(super) store: Option<Arc<UnifiedGraphStore>>,
89 pub(super) normal_only: bool,
90 pub(super) error_only: bool,
91 pub(super) max_length: Option<usize>,
92 pub(super) limit: Option<usize>,
93}
94
95impl PathBuilder {
96 pub fn normal_only(mut self) -> Self {
97 self.normal_only = true;
98 self.error_only = false;
99 self
100 }
101
102 pub fn error_only(mut self) -> Self {
103 self.normal_only = false;
104 self.error_only = true;
105 self
106 }
107
108 pub fn max_length(mut self, n: usize) -> Self {
109 self.max_length = Some(n);
110 self
111 }
112
113 pub fn limit(mut self, n: usize) -> Self {
114 self.limit = Some(n);
115 self
116 }
117
118 pub async fn execute(self) -> crate::error::Result<Vec<Path>> {
119 if let (Some(symbol), Some(store)) = (&self.function, &self.store) {
120 if let Some(cfg) = load_test_cfg(&store.db_path, symbol.0)? {
121 let mut paths = cfg.enumerate_paths();
122 if let Some(max) = self.max_length {
123 paths.retain(|p| p.blocks.len() <= max);
124 }
125 if let Some(limit) = self.limit {
126 paths.truncate(limit);
127 }
128 return Ok(paths);
129 }
130 let _ = symbol;
131 }
132
133 if let Some(symbol) = &self.function {
134 let entry = BlockId(symbol.0);
135 Ok(vec![Path {
136 id: PathId([0; 16]),
137 kind: PathKind::Normal,
138 blocks: vec![entry],
139 length: 1,
140 }])
141 } else {
142 Ok(Vec::new())
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_path_creation() {
153 let blocks = vec![BlockId(0), BlockId(1), BlockId(2)];
154 let path = Path::new(blocks.clone());
155
156 assert_eq!(path.blocks, blocks);
157 assert_eq!(path.length, 3);
158 assert!(path.is_normal());
159 assert!(!path.is_error());
160 }
161
162 #[test]
163 fn test_path_with_kind() {
164 let blocks = vec![BlockId(0), BlockId(1)];
165 let path = Path::with_kind(blocks.clone(), PathKind::Error);
166
167 assert_eq!(path.blocks, blocks);
168 assert_eq!(path.kind, PathKind::Error);
169 assert!(!path.is_normal());
170 assert!(path.is_error());
171 }
172
173 #[test]
174 fn test_path_contains() {
175 let path = Path::new(vec![BlockId(0), BlockId(1), BlockId(2)]);
176
177 assert!(path.contains(BlockId(0)));
178 assert!(path.contains(BlockId(1)));
179 assert!(path.contains(BlockId(2)));
180 assert!(!path.contains(BlockId(3)));
181 }
182
183 #[test]
184 fn test_path_entry_exit() {
185 let path = Path::new(vec![BlockId(0), BlockId(1), BlockId(2)]);
186
187 assert_eq!(path.entry(), Some(BlockId(0)));
188 assert_eq!(path.exit(), Some(BlockId(2)));
189 }
190
191 #[test]
192 fn test_path_id_stability() {
193 let blocks = vec![BlockId(0), BlockId(1), BlockId(2)];
194 let path1 = Path::new(blocks.clone());
195 let path2 = Path::new(blocks);
196
197 assert_eq!(path1.id, path2.id);
198 }
199
200 #[test]
201 fn test_path_id_uniqueness() {
202 let blocks1 = vec![BlockId(0), BlockId(1), BlockId(2)];
203 let blocks2 = vec![BlockId(0), BlockId(1), BlockId(3)];
204 let path1 = Path::new(blocks1);
205 let path2 = Path::new(blocks2);
206
207 assert_ne!(path1.id, path2.id);
208 }
209}