Skip to main content

ownsight_core/
model.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub struct VariableId(pub usize);
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct EventId(pub usize);
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct ScopeId(pub usize);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct FunctionId(pub usize);
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct Span {
18    pub file: String,
19    pub start_line: usize,
20    pub start_col: usize,
21    pub end_line: usize,
22    pub end_col: usize,
23}
24
25impl Span {
26    pub fn new(file: String, start_line: usize, start_col: usize, end_line: usize, end_col: usize) -> Self {
27        Self { file, start_line, start_col, end_line, end_col }
28    }
29    
30    pub fn single_line(file: String, line: usize, start_col: usize, end_col: usize) -> Self {
31        Self::new(file, line, start_col, line, end_col)
32    }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Variable {
37    pub id: VariableId,
38    pub name: String,
39    pub ty: String,
40    pub scope_id: ScopeId,
41    pub span: Span,
42    pub is_mutable: bool,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub enum EventKind {
47    Create,
48    MoveOut,
49    MoveIn,
50    BorrowShared,
51    BorrowMut,
52    Reborrow,
53    Use,
54    Drop,
55    StorageLive,
56    StorageDead,
57    Reinit,
58    Conflict,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Event {
63    pub id: EventId,
64    pub kind: EventKind,
65    pub variable_id: VariableId,
66    pub related_variable_id: Option<VariableId>,
67    pub span: Span,
68    pub explanation: String,
69    pub line_number: usize,
70}
71
72impl Event {
73    pub fn new(
74        id: EventId,
75        kind: EventKind,
76        variable_id: VariableId,
77        span: Span,
78        line_number: usize,
79        explanation: String,
80    ) -> Self {
81        Self {
82            id,
83            kind,
84            variable_id,
85            related_variable_id: None,
86            span,
87            explanation,
88            line_number,
89        }
90    }
91    
92    pub fn with_related(mut self, related: VariableId) -> Self {
93        self.related_variable_id = Some(related);
94        self
95    }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Scope {
100    pub id: ScopeId,
101    pub parent: Option<ScopeId>,
102    pub start_line: usize,
103    pub end_line: usize,
104    pub kind: ScopeKind,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub enum ScopeKind {
109    Function,
110    Block,
111    Loop,
112    If,
113    Match,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct FunctionInfo {
118    pub id: FunctionId,
119    pub name: String,
120    pub span: Span,
121    pub parameters: Vec<Parameter>,
122    pub return_type: String,
123    pub summary: Option<FunctionSummary>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct Parameter {
128    pub name: String,
129    pub ty: String,
130    pub ownership_behavior: OwnershipBehavior,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub enum OwnershipBehavior {
135    Consumes,
136    SharedBorrow,
137    MutableBorrow,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct FunctionSummary {
142    pub consumes: Vec<String>,
143    pub borrows_shared: Vec<String>,
144    pub borrows_mut: Vec<String>,
145    pub returns_ownership: bool,
146    pub references_escape: bool,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct SourceFile {
151    pub path: String,
152    pub content: String,
153    pub lines: Vec<String>,
154}
155
156impl SourceFile {
157    pub fn new(path: String, content: String) -> Self {
158        let lines = content.lines().map(|s| s.to_string()).collect();
159        Self { path, content, lines }
160    }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct Diagnostic {
165    pub level: DiagnosticLevel,
166    pub message: String,
167    pub span: Span,
168    pub code: Option<String>,
169    pub suggestion: Option<String>,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173pub enum DiagnosticLevel {
174    Error,
175    Warning,
176    Note,
177    Help,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ProgramAnalysis {
182    pub files: Vec<SourceFile>,
183    pub functions: Vec<FunctionInfo>,
184    pub variables: Vec<Variable>,
185    pub scopes: Vec<Scope>,
186    pub events: Vec<Event>,
187    pub ownership_graph: crate::graph::OwnershipGraph,
188    pub diagnostics: Vec<Diagnostic>,
189    pub metadata: AnalysisMetadata,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct AnalysisMetadata {
194    pub mode: AnalysisMode,
195    pub timestamp: String,
196    pub version: String,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
200pub enum AnalysisMode {
201    Teaching,
202    Debug,
203}
204
205impl ProgramAnalysis {
206    pub fn new(mode: AnalysisMode) -> Self {
207        Self {
208            files: Vec::new(),
209            functions: Vec::new(),
210            variables: Vec::new(),
211            scopes: Vec::new(),
212            events: Vec::new(),
213            ownership_graph: crate::graph::OwnershipGraph::new(),
214            diagnostics: Vec::new(),
215            metadata: AnalysisMetadata {
216                mode,
217                timestamp: chrono::Utc::now().to_rfc3339(),
218                version: env!("CARGO_PKG_VERSION").to_string(),
219            },
220        }
221    }
222    
223    pub fn get_variable(&self, id: VariableId) -> Option<&Variable> {
224        self.variables.iter().find(|v| v.id == id)
225    }
226    
227    pub fn get_events_for_variable(&self, id: VariableId) -> Vec<&Event> {
228        self.events.iter()
229            .filter(|e| e.variable_id == id || e.related_variable_id == Some(id))
230            .collect()
231    }
232    
233    pub fn get_events_at_line(&self, line: usize) -> Vec<&Event> {
234        self.events.iter()
235            .filter(|e| e.line_number == line)
236            .collect()
237    }
238    
239    pub fn get_ownership_state_at_line(&self, line: usize) -> OwnershipState {
240        let mut state = OwnershipState::new();
241        
242        for event in self.events.iter().filter(|e| e.line_number <= line) {
243            state.apply_event(event);
244        }
245        
246        state
247    }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct OwnershipState {
252    pub valid_variables: HashMap<VariableId, VariableState>,
253    pub active_borrows: Vec<BorrowInfo>,
254}
255
256impl OwnershipState {
257    pub fn new() -> Self {
258        Self {
259            valid_variables: HashMap::new(),
260            active_borrows: Vec::new(),
261        }
262    }
263    
264    pub fn apply_event(&mut self, event: &Event) {
265        match event.kind {
266            EventKind::Create | EventKind::StorageLive => {
267                self.valid_variables.insert(event.variable_id, VariableState::Valid);
268            }
269            EventKind::MoveOut => {
270                self.valid_variables.insert(event.variable_id, VariableState::MovedOut);
271            }
272            EventKind::MoveIn => {
273                self.valid_variables.insert(event.variable_id, VariableState::Valid);
274            }
275            EventKind::BorrowShared => {
276                self.active_borrows.push(BorrowInfo {
277                    borrowed_var: event.variable_id,
278                    borrow_var: event.related_variable_id,
279                    is_mutable: false,
280                });
281            }
282            EventKind::BorrowMut => {
283                self.active_borrows.push(BorrowInfo {
284                    borrowed_var: event.variable_id,
285                    borrow_var: event.related_variable_id,
286                    is_mutable: true,
287                });
288            }
289            EventKind::Drop | EventKind::StorageDead => {
290                self.valid_variables.remove(&event.variable_id);
291                self.active_borrows.retain(|b| b.borrowed_var != event.variable_id);
292            }
293            EventKind::Reinit => {
294                self.valid_variables.insert(event.variable_id, VariableState::Valid);
295            }
296            _ => {}
297        }
298    }
299    
300    pub fn is_valid(&self, var_id: VariableId) -> bool {
301        matches!(self.valid_variables.get(&var_id), Some(VariableState::Valid))
302    }
303    
304    pub fn is_moved(&self, var_id: VariableId) -> bool {
305        matches!(self.valid_variables.get(&var_id), Some(VariableState::MovedOut))
306    }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
310pub enum VariableState {
311    Valid,
312    MovedOut,
313    PartiallyMoved,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct BorrowInfo {
318    pub borrowed_var: VariableId,
319    pub borrow_var: Option<VariableId>,
320    pub is_mutable: bool,
321}