- Created .gitea/workflows/ci.yml with lint, build, and test jobs - Fixed scope extraction bug in analyzer.rs (src/main.rs now returns "src") - Fixed renamed file path assignment in git.rs (correct old_path/new_path)
173 lines
5.0 KiB
Rust
173 lines
5.0 KiB
Rust
use anyhow::{Context, Result};
|
|
use std::process::Command;
|
|
use std::string::ToString;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct GitCommit {
|
|
pub old_path: Option<String>,
|
|
pub new_path: Option<String>,
|
|
pub sha: String,
|
|
pub message: String,
|
|
pub author: String,
|
|
}
|
|
|
|
pub fn get_recent_commits(limit: usize) -> Result<Vec<GitCommit>> {
|
|
let output = Command::new("git")
|
|
.args(&[
|
|
"log",
|
|
"--pretty=format:%H|%s|%an",
|
|
&format!("-n{}", limit),
|
|
])
|
|
.output()
|
|
.context("Failed to execute git log")?;
|
|
|
|
if !output.status.success() {
|
|
return Err(anyhow::anyhow!("Git log command failed"));
|
|
}
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let mut commits = Vec::new();
|
|
|
|
for line in stdout.lines() {
|
|
let parts: Vec<&str> = line.splitn(3, "|").collect();
|
|
if parts.len() >= 3 {
|
|
commits.push(GitCommit {
|
|
old_path: None,
|
|
new_path: None,
|
|
sha: parts[0].to_string(),
|
|
message: parts[1].to_string(),
|
|
author: parts[2].to_string(),
|
|
});
|
|
}
|
|
}
|
|
|
|
Ok(commits)
|
|
}
|
|
|
|
pub fn get_diff_from_sha(sha: &str) -> Result<Vec<Change>> {
|
|
let output = Command::new("git")
|
|
.args(&["show", "--stat", sha])
|
|
.output()
|
|
.context("Failed to execute git show")?;
|
|
|
|
if !output.status.success() {
|
|
return Err(anyhow::anyhow!("Git show command failed"));
|
|
}
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let mut changes = Vec::new();
|
|
|
|
for line in stdout.lines() {
|
|
if line.contains("|") {
|
|
let parts: Vec<&str> = line.split("|").collect();
|
|
if parts.len() == 2 {
|
|
let path_parts: Vec<&str> = parts[0].split(" -> ").collect();
|
|
let (old_path, new_path) = if path_parts.len() == 2 {
|
|
(Some(path_parts[0].to_string()), Some(path_parts[1].to_string()))
|
|
} else {
|
|
(None, Some(parts[0].to_string()))
|
|
};
|
|
|
|
changes.push(Change {
|
|
old_path,
|
|
new_path,
|
|
additions: 0,
|
|
deletions: 0,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(changes)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Change {
|
|
pub old_path: Option<String>,
|
|
pub new_path: Option<String>,
|
|
pub additions: usize,
|
|
pub deletions: usize,
|
|
}
|
|
|
|
pub fn get_staged_diff() -> Result<Vec<Change>> {
|
|
let output = Command::new("git")
|
|
.args(&["diff", "--cached", "--stat"])
|
|
.output()
|
|
.context("Failed to execute git diff --cached")?;
|
|
|
|
if !output.status.success() {
|
|
return Err(anyhow::anyhow!("Git diff command failed"));
|
|
}
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let mut changes = Vec::new();
|
|
|
|
for line in stdout.lines() {
|
|
let parts: Vec<&str> = line.split("|").collect();
|
|
if parts.len() == 2 {
|
|
let path_parts: Vec<&str> = parts[0].split(" -> ").collect();
|
|
let (old_path, new_path) = if path_parts.len() == 2 {
|
|
(Some(path_parts[0].to_string()), Some(path_parts[1].to_string()))
|
|
} else {
|
|
(None, Some(parts[0].to_string()))
|
|
};
|
|
|
|
let stat_parts: Vec<&str> = parts[1].trim().split(" ").collect();
|
|
let mut additions = 0;
|
|
let mut deletions = 0;
|
|
|
|
for stat in stat_parts {
|
|
if stat.contains('+') && stat.parse::<usize>().is_ok() {
|
|
additions = stat.replace('+', "").parse().unwrap_or(0);
|
|
}
|
|
if stat.contains('-') && stat.parse::<usize>().is_ok() {
|
|
deletions = stat.replace('-', "").parse().unwrap_or(0);
|
|
}
|
|
}
|
|
|
|
changes.push(Change {
|
|
old_path,
|
|
new_path,
|
|
additions,
|
|
deletions,
|
|
});
|
|
}
|
|
}
|
|
|
|
Ok(changes)
|
|
}
|
|
|
|
pub fn get_diff_stats() -> Result<(usize, usize)> {
|
|
let output = Command::new("git")
|
|
.args(&["diff", "--shortstat"])
|
|
.output()
|
|
.context("Failed to execute git diff")?;
|
|
|
|
if !output.status.success() {
|
|
return Err(anyhow::anyhow!("Git diff command failed"));
|
|
}
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let mut insertions = 0;
|
|
let mut deletions = 0;
|
|
|
|
for line in stdout.lines() {
|
|
let parts: Vec<&str> = line.split(",").collect();
|
|
for part in parts {
|
|
let part = part.trim();
|
|
if let Some(insertions_str) = part.strip_suffix(" insertions(+)") {
|
|
if let Ok(count) = insertions_str.trim().parse::<usize>() {
|
|
insertions = count;
|
|
}
|
|
}
|
|
if let Some(deletions_str) = part.strip_suffix(" deletions(-)") {
|
|
if let Ok(count) = deletions_str.trim().parse::<usize>() {
|
|
deletions = count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok((insertions, deletions))
|
|
}
|