ids.rs 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. //! Node-ID generation — MUST produce byte-identical output to
  2. //! `generateNodeId` in `src/extraction/tree-sitter-helpers.ts`:
  3. //!
  4. //! `${kind}:${sha256(`${filePath}:${kind}:${name}:${line}`).hex[0..32]}`
  5. //!
  6. //! and the file-node special case in `TreeSitterExtractor.extract()`:
  7. //!
  8. //! `file:${filePath}`
  9. //!
  10. //! Node identity is how the wasm path and the kernel path agree on the same
  11. //! graph — a drift here breaks every edge. Pinned by the node-id parity test
  12. //! in `__tests__/kernel-scaffold.test.ts`.
  13. use sha2::{Digest, Sha256};
  14. pub fn node_id(file_path: &str, kind: &str, name: &str, line: u32) -> String {
  15. let mut hasher = Sha256::new();
  16. hasher.update(file_path.as_bytes());
  17. hasher.update(b":");
  18. hasher.update(kind.as_bytes());
  19. hasher.update(b":");
  20. hasher.update(name.as_bytes());
  21. hasher.update(b":");
  22. hasher.update(line.to_string().as_bytes());
  23. let digest = hasher.finalize();
  24. // 32 hex chars = first 16 bytes.
  25. let mut hex = String::with_capacity(kind.len() + 1 + 32);
  26. hex.push_str(kind);
  27. hex.push(':');
  28. for b in &digest[..16] {
  29. hex.push_str(&format!("{b:02x}"));
  30. }
  31. hex
  32. }
  33. pub fn file_node_id(file_path: &str) -> String {
  34. format!("file:{file_path}")
  35. }
  36. #[cfg(test)]
  37. mod tests {
  38. use super::*;
  39. #[test]
  40. fn matches_known_ts_output() {
  41. // Pinned vector: node -e "crypto.createHash('sha256')
  42. // .update('src/a.ts:function:foo:3').digest('hex').substring(0,32)"
  43. assert_eq!(
  44. node_id("src/a.ts", "function", "foo", 3),
  45. "function:bfb15544fed707794274a5c61006ea7b"
  46. );
  47. }
  48. }