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 #[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
38pub 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
50pub 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}