1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#![cfg_attr(coverage_nightly, coverage(off))]
//! Core types for AI-ready context generation.
//!
//! This module contains the fundamental data structures used for representing
//! project context, including AST items, file contexts, and project summaries.
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProjectContext {
pub project_type: String,
pub files: Vec<FileContext>,
pub summary: ProjectSummary,
/// O(1) graph for symbol lookups and PageRank-based importance
#[serde(skip)]
pub graph: Option<crate::services::context_graph::ProjectContextGraph>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProjectSummary {
pub total_files: usize,
pub total_functions: usize,
pub total_structs: usize,
pub total_enums: usize,
pub total_traits: usize,
pub total_impls: usize,
pub dependencies: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FileContext {
pub path: String,
pub language: String,
pub items: Vec<AstItem>,
#[serde(skip_serializing_if = "Option::is_none")]
pub complexity_metrics: Option<crate::services::complexity::FileComplexityMetrics>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type")]
#[derive(PartialEq)]
pub enum AstItem {
Function {
name: String,
visibility: String,
is_async: bool,
line: usize,
},
Struct {
name: String,
visibility: String,
fields_count: usize,
derives: Vec<String>,
line: usize,
},
Enum {
name: String,
visibility: String,
variants_count: usize,
line: usize,
},
Trait {
name: String,
visibility: String,
line: usize,
},
Impl {
type_name: String,
trait_name: Option<String>,
line: usize,
},
Module {
name: String,
visibility: String,
line: usize,
},
Use {
path: String,
line: usize,
},
/// Import statement for language-specific module imports
///
/// Supports various import patterns across different languages including
/// Python, JavaScript, TypeScript, and other languages with module systems.
///
/// # Examples
///
/// ## Python Import Patterns
///
/// ```
/// use pmat::services::context::AstItem;
///
/// // Simple import: import os
/// let import1 = AstItem::Import {
/// module: "os".to_string(),
/// items: vec![],
/// alias: None,
/// line: 1,
/// };
/// assert_eq!(import1.display_name(), "os");
///
/// // Import with alias: import numpy as np
/// let import2 = AstItem::Import {
/// module: "numpy".to_string(),
/// items: vec![],
/// alias: Some("np".to_string()),
/// line: 2,
/// };
/// assert_eq!(import2.display_name(), "numpy");
///
/// // From import: from typing import List, Dict
/// let import3 = AstItem::Import {
/// module: "typing".to_string(),
/// items: vec!["List".to_string(), "Dict".to_string()],
/// alias: None,
/// line: 3,
/// };
/// assert_eq!(import3.display_name(), "typing");
///
/// // Submodule import: import os.path
/// let import4 = AstItem::Import {
/// module: "os.path".to_string(),
/// items: vec![],
/// alias: None,
/// line: 4,
/// };
/// assert_eq!(import4.display_name(), "os.path");
///
/// // Wildcard import: from math import *
/// let import5 = AstItem::Import {
/// module: "math".to_string(),
/// items: vec!["*".to_string()],
/// alias: None,
/// line: 5,
/// };
/// assert_eq!(import5.display_name(), "math");
/// ```
///
/// ## JavaScript/TypeScript Import Patterns
///
/// ```
/// use pmat::services::context::AstItem;
///
/// // ES6 default import: import React from 'react'
/// let import1 = AstItem::Import {
/// module: "react".to_string(),
/// items: vec![],
/// alias: None,
/// line: 1,
/// };
/// assert_eq!(import1.display_name(), "react");
///
/// // Named imports: import { useState, useEffect } from 'react'
/// let import2 = AstItem::Import {
/// module: "react".to_string(),
/// items: vec!["useState".to_string(), "useEffect".to_string()],
/// alias: None,
/// line: 2,
/// };
/// assert_eq!(import2.display_name(), "react");
///
/// // Scoped package: import { Button } from '@mui/material'
/// let import3 = AstItem::Import {
/// module: "@mui/material".to_string(),
/// items: vec!["Button".to_string()],
/// alias: None,
/// line: 3,
/// };
/// assert_eq!(import3.display_name(), "@mui/material");
///
/// // Relative import: import { utils } from './utils'
/// let import4 = AstItem::Import {
/// module: "./utils".to_string(),
/// items: vec!["utils".to_string()],
/// alias: None,
/// line: 4,
/// };
/// assert_eq!(import4.display_name(), "./utils");
///
/// // Parent directory import: import Component from '../components/Button'
/// let import5 = AstItem::Import {
/// module: "../components/Button".to_string(),
/// items: vec![],
/// alias: None,
/// line: 5,
/// };
/// assert_eq!(import5.display_name(), "../components/Button");
/// ```
///
/// ## Edge Cases and Special Patterns
///
/// ```
/// use pmat::services::context::AstItem;
///
/// // Empty module (edge case)
/// let import1 = AstItem::Import {
/// module: "".to_string(),
/// items: vec![],
/// alias: None,
/// line: 1,
/// };
/// assert_eq!(import1.display_name(), "");
///
/// // Complex nested module path
/// let import2 = AstItem::Import {
/// module: "matplotlib.pyplot.subplots".to_string(),
/// items: vec![],
/// alias: Some("plt".to_string()),
/// line: 2,
/// };
/// assert_eq!(import2.display_name(), "matplotlib.pyplot.subplots");
///
/// // Multiple aliases don't affect display_name
/// let import3 = AstItem::Import {
/// module: "pandas".to_string(),
/// items: vec!["DataFrame".to_string(), "Series".to_string()],
/// alias: Some("pd".to_string()),
/// line: 3,
/// };
/// assert_eq!(import3.display_name(), "pandas");
/// ```
Import {
/// What is being imported (module, package, or specific items)
module: String,
/// Specific items imported from the module (if any)
items: Vec<String>,
/// Alias for the import (if any)
alias: Option<String>,
/// Line number
line: usize,
},
}
impl AstItem {
#[must_use]
pub fn display_name(&self) -> &str {
match self {
AstItem::Function { name, .. } => name,
AstItem::Struct { name, .. } => name,
AstItem::Enum { name, .. } => name,
AstItem::Trait { name, .. } => name,
AstItem::Impl { type_name, .. } => type_name,
AstItem::Module { name, .. } => name,
AstItem::Use { path, .. } => path,
AstItem::Import { module, .. } => module,
}
}
}
/// Grouped items by type for formatting
pub(crate) struct GroupedItems<'a> {
pub functions: Vec<&'a AstItem>,
pub structs: Vec<&'a AstItem>,
pub enums: Vec<&'a AstItem>,
pub traits: Vec<&'a AstItem>,
pub impls: Vec<&'a AstItem>,
pub modules: Vec<&'a AstItem>,
}
impl<'a> GroupedItems<'a> {
pub fn new() -> Self {
Self {
functions: Vec::new(),
structs: Vec::new(),
enums: Vec::new(),
traits: Vec::new(),
impls: Vec::new(),
modules: Vec::new(),
}
}
}