Skip to main content

sem_core/model/
entity.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5#[serde(rename_all = "camelCase")]
6pub struct SemanticEntity {
7    pub id: String,
8    pub file_path: String,
9    pub entity_type: String,
10    pub name: String,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub parent_id: Option<String>,
13    pub content: String,
14    pub content_hash: String,
15    /// AST-based hash that strips comments and normalizes whitespace.
16    /// Two entities with the same structural_hash are logically identical
17    /// even if formatting/comments differ. Inspired by Unison's content-addressed model.
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub structural_hash: Option<String>,
20    pub start_line: usize,
21    pub end_line: usize,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub metadata: Option<HashMap<String, String>>,
24}
25
26pub fn build_entity_id(
27    file_path: &str,
28    entity_type: &str,
29    name: &str,
30    parent_id: Option<&str>,
31) -> String {
32    match parent_id {
33        Some(pid) => format!("{pid}::{name}"),
34        None => format!("{file_path}::{entity_type}::{name}"),
35    }
36}
37
38/// Build an entity ID with a line-number disambiguator for overloads.
39pub fn build_entity_id_disambiguated(
40    file_path: &str,
41    entity_type: &str,
42    name: &str,
43    parent_id: Option<&str>,
44    line: usize,
45) -> String {
46    let base = build_entity_id(file_path, entity_type, name, parent_id);
47    format!("{base}@L{line}")
48}
49
50/// Build an entity ID with a line-number and same-line ordinal disambiguator.
51pub fn build_entity_id_disambiguated_with_ordinal(
52    file_path: &str,
53    entity_type: &str,
54    name: &str,
55    parent_id: Option<&str>,
56    line: usize,
57    ordinal: usize,
58) -> String {
59    let base = build_entity_id_disambiguated(file_path, entity_type, name, parent_id, line);
60    format!("{base}#{ordinal}")
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_build_entity_id_no_parent() {
69        assert_eq!(
70            build_entity_id("src/main.ts", "function", "hello", None),
71            "src/main.ts::function::hello"
72        );
73    }
74
75    #[test]
76    fn test_build_entity_id_with_parent() {
77        let id = build_entity_id(
78            "src/main.ts",
79            "method",
80            "greet",
81            Some("src/main.ts::class::MyClass"),
82        );
83        assert_eq!(id, "src/main.ts::class::MyClass::greet");
84    }
85}