Skip to main content

sem_core/model/
change.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4#[serde(rename_all = "lowercase")]
5pub enum ChangeType {
6    Added,
7    Modified,
8    Deleted,
9    Moved,
10    Renamed,
11    Reordered,
12}
13
14impl std::fmt::Display for ChangeType {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            ChangeType::Added => write!(f, "added"),
18            ChangeType::Modified => write!(f, "modified"),
19            ChangeType::Deleted => write!(f, "deleted"),
20            ChangeType::Moved => write!(f, "moved"),
21            ChangeType::Renamed => write!(f, "renamed"),
22            ChangeType::Reordered => write!(f, "reordered"),
23        }
24    }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29#[non_exhaustive]
30pub struct SemanticChange {
31    pub id: String,
32    pub entity_id: String,
33    pub change_type: ChangeType,
34    pub entity_type: String,
35    pub entity_name: String,
36    #[serde(default)]
37    pub entity_line: usize,
38    #[serde(default)]
39    pub start_line: usize,
40    #[serde(default)]
41    pub end_line: usize,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub old_start_line: Option<usize>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub old_end_line: Option<usize>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub parent_name: Option<String>,
48    pub file_path: String,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub old_entity_name: Option<String>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub old_file_path: Option<String>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub old_parent_id: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub before_content: Option<String>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub after_content: Option<String>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub commit_sha: Option<String>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub author: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub timestamp: Option<String>,
65    /// Whether the AST structure changed (true) or only formatting/comments (false).
66    /// None when structural hash is unavailable.
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub structural_change: Option<bool>,
69}
70
71impl SemanticChange {
72    pub fn has_content_change(&self) -> bool {
73        match (&self.before_content, &self.after_content) {
74            (Some(before), Some(after)) => before != after,
75            _ => false,
76        }
77    }
78}