aurora_autodebug/
analyzer.rs1use std::fs;
2use std::path::Path;
3use std::io::{BufRead, BufReader};
4use aurora_core::{AuroraResult, AuroraError, Pipeline, Value};
5use regex::Regex;
6
7pub fn analyze_target(target: &str) -> AuroraResult<Pipeline> {
8 if target.chars().all(|c| c.is_ascii_digit()) {
9 return analyze_process(target);
10 }
11
12 let path = Path::new(target);
13 if path.exists() {
14 if path.is_file() {
15 let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
16 match ext {
17 "log" | "txt" => analyze_log_file(target),
18 _ => {
19 let size = fs::metadata(target)
20 .map(|m| m.len())
21 .unwrap_or(0);
22 Ok(Pipeline::single(Value::String(
23 format!("File: {target} ({size} bytes). Use 'aurora autodebug log {target}' to analyze.")
24 )))
25 }
26 }
27 } else {
28 Err(AuroraError::AutoDebugError(format!("Not a file: {target}")))
29 }
30 } else {
31 Err(AuroraError::NotFound(format!("Target not found: {target}")))
32 }
33}
34
35fn analyze_process(pid: &str) -> AuroraResult<Pipeline> {
36 let status_path = format!("/proc/{pid}/status");
37 let status = match fs::read_to_string(&status_path) {
38 Ok(s) => s,
39 Err(_) => return Err(AuroraError::AutoDebugError(format!("Process {pid} not found"))),
40 };
41
42 let mut results = Vec::new();
43 for line in status.lines() {
44 if let Some((key, val)) = line.split_once(":\t") {
45 let mut record = std::collections::BTreeMap::new();
46 record.insert("key".to_string(), Value::String(key.trim().to_string()));
47 record.insert("value".to_string(), Value::String(val.trim().to_string()));
48 results.push(Value::Record(record));
49 }
50 }
51
52 Ok(Pipeline::table(
53 vec!["key".to_string(), "value".to_string()],
54 results.into_iter().map(|r| {
55 match r {
56 Value::Record(map) => {
57 vec![
58 map.get("key").cloned().unwrap_or(Value::Null),
59 map.get("value").cloned().unwrap_or(Value::Null),
60 ]
61 }
62 _ => vec![],
63 }
64 }).collect(),
65 ))
66}
67
68pub fn analyze_log_file(path: &str) -> AuroraResult<Pipeline> {
69 let file = fs::File::open(path)
70 .map_err(|e| AuroraError::AutoDebugError(format!("Cannot open log: {e}")))?;
71 let reader = BufReader::new(file);
72
73 let error_re = Regex::new(r"(?i)(ERROR|FATAL|PANIC|CRASH|SEGFAULT|SEGV|CORE DUMP|KILLED|OOM)").ok();
74 let warn_re = Regex::new(r"(?i)(WARNING|WARN|DEPRECATED|SLOW)").ok();
75
76 let mut results = Vec::new();
77 for (i, line_result) in reader.lines().enumerate() {
78 let line = line_result.map_err(|e| AuroraError::AutoDebugError(format!("Read error: {e}")))?;
79 if let Some(ref re) = error_re {
80 if let Some(m) = re.find(&line) {
81 let mut record = std::collections::BTreeMap::new();
82 record.insert("severity".to_string(), Value::String("ERROR".to_string()));
83 record.insert("line".to_string(), Value::Int((i + 1) as i64));
84 record.insert("match".to_string(), Value::String(m.as_str().to_string()));
85 record.insert("content".to_string(), Value::String(line.clone()));
86 results.push(Value::Record(record));
87 }
88 }
89 if let Some(ref re) = warn_re {
90 if let Some(m) = re.find(&line) {
91 let mut record = std::collections::BTreeMap::new();
92 record.insert("severity".to_string(), Value::String("WARNING".to_string()));
93 record.insert("line".to_string(), Value::Int((i + 1) as i64));
94 record.insert("match".to_string(), Value::String(m.as_str().to_string()));
95 record.insert("content".to_string(), Value::String(line));
96 results.push(Value::Record(record));
97 }
98 }
99 }
100
101 if results.is_empty() {
102 return Ok(Pipeline::single(Value::String(
103 "No errors or warnings found in log.".to_string()
104 )));
105 }
106
107 Ok(Pipeline::table(
108 vec!["severity".to_string(), "line".to_string(), "match".to_string(), "content".to_string()],
109 results.into_iter().map(|r| {
110 match r {
111 Value::Record(map) => {
112 vec![
113 map.get("severity").cloned().unwrap_or(Value::Null),
114 map.get("line").cloned().unwrap_or(Value::Null),
115 map.get("match").cloned().unwrap_or(Value::Null),
116 map.get("content").cloned().unwrap_or(Value::Null),
117 ]
118 }
119 _ => vec![],
120 }
121 }).collect(),
122 ))
123}
124
125pub fn find_log_files(path: &str) -> Vec<String> {
126 let mut logs = Vec::new();
127 if let Ok(entries) = fs::read_dir(path) {
128 for entry in entries.flatten() {
129 let p = entry.path();
130 if let Some(ext) = p.extension() {
131 if ext == "log" {
132 logs.push(p.to_string_lossy().to_string());
133 }
134 }
135 }
136 }
137 logs
138}