-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathanalyzer.go
More file actions
205 lines (172 loc) · 6.06 KB
/
analyzer.go
File metadata and controls
205 lines (172 loc) · 6.06 KB
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
package main
import (
"fmt"
"path/filepath"
"time"
"gopkg.in/yaml.v3"
"io/ioutil"
)
type Config struct {
Database struct {
Neo4j struct {
URI string `yaml:"uri"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Database string `yaml:"database"`
} `yaml:"neo4j"`
} `yaml:"database"`
Analyzer struct {
SourceDir string `yaml:"source_dir"`
} `yaml:"analyzer"`
Web struct {
Port string `yaml:"port"`
Host string `yaml:"host"`
} `yaml:"web"`
Parsing struct {
MaxComplexityThreshold int `yaml:"max_complexity_threshold"`
SkipTestFiles bool `yaml:"skip_test_files"`
SkipVendorDirs bool `yaml:"skip_vendor_dirs"`
} `yaml:"parsing"`
}
func LoadConfig(configPath string) (*Config, error) {
data, err := ioutil.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
var config Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
return &config, nil
}
type AnalysisProgress struct {
Stage string `json:"stage"`
CurrentStep int `json:"current_step"`
TotalSteps int `json:"total_steps"`
FunctionsFound int `json:"functions_found"`
RelationsFound int `json:"relations_found"`
IsComplete bool `json:"is_complete"`
Message string `json:"message"`
RecentLogs []string `json:"recent_logs,omitempty"`
}
type Analyzer struct {
config *Config
gitRepo *GitRepository
astParser *ASTParser
graphDB *GraphDatabase
progress *AnalysisProgress
logBuffer []string
}
func NewAnalyzer(config *Config) *Analyzer {
if config == nil {
// Provide default configuration if nil
config = &Config{}
config.Analyzer.SourceDir = "../source"
config.Database.Neo4j.URI = "neo4j://localhost:7687"
config.Database.Neo4j.Username = "neo4j"
config.Database.Neo4j.Password = "password"
}
analyzer := &Analyzer{
config: config,
gitRepo: NewGitRepository(config.Analyzer.SourceDir),
astParser: NewASTParser(),
graphDB: NewGraphDatabase(config.Database.Neo4j.URI, config.Database.Neo4j.Username, config.Database.Neo4j.Password),
progress: &AnalysisProgress{Stage: "ready", TotalSteps: 4},
logBuffer: make([]string, 0, 20), // Keep last 20 log messages
}
// Set up git progress callback
analyzer.gitRepo.SetProgressCallback(analyzer.addLog)
return analyzer
}
func (a *Analyzer) AnalyzeRepository(repoURL string) error {
// Check for nil analyzer components
if a == nil || a.gitRepo == nil || a.astParser == nil || a.graphDB == nil {
return fmt.Errorf("analyzer not properly initialized")
}
// Step 1: Cloning
a.progress.Stage = "cloning"
a.progress.CurrentStep = 1
a.progress.Message = "Cloning repository..."
a.addLog(fmt.Sprintf("Cloning repository: %s", repoURL))
fmt.Printf("Cloning repository: %s\n", repoURL)
repoPath, err := a.gitRepo.Clone(repoURL)
if err != nil {
return fmt.Errorf("failed to clone repository: %w", err)
}
a.addLog(fmt.Sprintf("Repository cloned to: %s", repoPath))
fmt.Printf("Repository cloned to: %s\n", repoPath)
// Step 2: Connecting to database
a.progress.Stage = "connecting"
a.progress.CurrentStep = 2
a.progress.Message = "Connecting to Neo4j database..."
a.addLog("Connecting to Neo4j database...")
fmt.Println("Connecting to Neo4j database...")
// Create a fresh database connection for this analysis
freshDB := NewGraphDatabase(a.config.Database.Neo4j.URI, a.config.Database.Neo4j.Username, a.config.Database.Neo4j.Password)
if err := freshDB.Connect(); err != nil {
return fmt.Errorf("failed to connect to Neo4j: %w", err)
}
defer freshDB.Close() // Ensure connection is closed when function exits
// Step 3: Parsing
a.progress.Stage = "parsing"
a.progress.CurrentStep = 3
a.progress.Message = "Parsing Go files and extracting functions..."
a.addLog("Parsing Go files and extracting functions...")
fmt.Println("Parsing Go files and extracting functions...")
functions, relationships, err := a.astParser.ParseProject(repoPath)
if err != nil {
return fmt.Errorf("failed to parse project: %w", err)
}
// Update progress with real numbers
a.progress.FunctionsFound = len(functions)
a.progress.RelationsFound = len(relationships)
a.addLog(fmt.Sprintf("Found %d functions and %d relationships", len(functions), len(relationships)))
fmt.Printf("Found %d functions and %d relationships\n", len(functions), len(relationships))
// Step 4: Storing data
a.progress.Stage = "storing"
a.progress.CurrentStep = 4
a.progress.Message = "Storing data in graph database..."
a.addLog("Storing data in graph database...")
fmt.Println("Storing data in graph database...")
if err := freshDB.StoreFunctions(functions); err != nil {
a.addLog(fmt.Sprintf("❌ Failed to store functions: %v", err))
return fmt.Errorf("failed to store functions: %w", err)
}
a.addLog("✅ Functions stored successfully")
if err := freshDB.StoreRelationships(relationships); err != nil {
a.addLog(fmt.Sprintf("❌ Failed to store relationships: %v", err))
return fmt.Errorf("failed to store relationships: %w", err)
}
a.addLog("✅ Relationships stored successfully")
// Mark as complete
a.progress.Stage = "complete"
a.progress.IsComplete = true
a.progress.Message = "Analysis completed successfully!"
a.addLog("✅ Analysis completed successfully!")
return nil
}
func (a *Analyzer) addLog(message string) {
if a == nil {
return
}
timestamp := time.Now().Format("15:04:05")
logEntry := fmt.Sprintf("[%s] %s", timestamp, message)
// Add to buffer, keeping only last 20 messages
a.logBuffer = append(a.logBuffer, logEntry)
if len(a.logBuffer) > 20 {
a.logBuffer = a.logBuffer[1:]
}
}
func (a *Analyzer) GetProgress() *AnalysisProgress {
if a == nil || a.progress == nil {
return &AnalysisProgress{Stage: "ready", TotalSteps: 4}
}
// Include recent logs
progress := *a.progress // Copy the struct
progress.RecentLogs = make([]string, len(a.logBuffer))
copy(progress.RecentLogs, a.logBuffer)
return &progress
}
func getRepoName(repoURL string) string {
return filepath.Base(repoURL)
}