Initial upload: GitPulse - Developer Productivity Analyzer CLI tool
This commit is contained in:
158
src/commands/export.rs
Normal file
158
src/commands/export.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
use crate::cli::export::ExportArgs;
|
||||
use crate::config::Config;
|
||||
use crate::git::Repository;
|
||||
use crate::models::AnalysisResult;
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub mod export_args {
|
||||
use clap::{Args, Parser};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct ExportArgs {
|
||||
#[arg(long, value_name = "FORMAT")]
|
||||
#[arg(help = "Export format: json, csv")]
|
||||
pub format: String,
|
||||
|
||||
#[arg(short, long, value_name = "PATH")]
|
||||
#[arg(help = "Output file path")]
|
||||
pub output: Option<PathBuf>,
|
||||
|
||||
#[arg(long)]
|
||||
#[arg(help = "Include commit history")]
|
||||
pub history: bool,
|
||||
|
||||
#[arg(long)]
|
||||
#[arg(help = "Include refactoring data")]
|
||||
pub refactor: bool,
|
||||
}
|
||||
}
|
||||
|
||||
pub use export_args::ExportArgs;
|
||||
|
||||
pub fn run(
|
||||
repo_path: Option<PathBuf>,
|
||||
args: ExportArgs,
|
||||
config: &Config,
|
||||
verbose: bool,
|
||||
) -> Result<()> {
|
||||
let repo = Repository::new(repo_path)?;
|
||||
|
||||
if verbose {
|
||||
eprintln!("Exporting from repository at: {}", repo.path().display());
|
||||
}
|
||||
|
||||
let result = crate::commands::analyze::run(
|
||||
Some(repo.path().clone()),
|
||||
crate::commands::analyze::AnalyzeArgs {
|
||||
since: None,
|
||||
until: None,
|
||||
commits: None,
|
||||
include_merges: false,
|
||||
no_churn: false,
|
||||
no_refactor: !args.refactor,
|
||||
json: true,
|
||||
history: args.history,
|
||||
top: None,
|
||||
output: None,
|
||||
},
|
||||
config,
|
||||
verbose,
|
||||
)?;
|
||||
|
||||
match args.format.to_lowercase().as_str() {
|
||||
"json" | "j" => {
|
||||
if let Some(output) = args.output {
|
||||
export::export_json_to_file(&result, &output)?;
|
||||
if verbose {
|
||||
eprintln!("Exported to {}", output.display());
|
||||
}
|
||||
} else {
|
||||
export::export_json(&result, None)?;
|
||||
}
|
||||
}
|
||||
"csv" | "c" => {
|
||||
if let Some(output) = args.output {
|
||||
export::export_csv_to_file(&result, &output)?;
|
||||
if verbose {
|
||||
eprintln!("Exported to {}", output.display());
|
||||
}
|
||||
} else {
|
||||
println!("CSV export requires an output file. Use --output <file.csv>");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
anyhow::bail!("Unsupported format: {}. Use 'json' or 'csv'.", args.format);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod export {
|
||||
use crate::models::AnalysisResult;
|
||||
use anyhow::{Context, Result};
|
||||
use serde_json;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn export_json(result: &AnalysisResult, output: Option<PathBuf>) -> Result<()> {
|
||||
let json = serde_json::to_string_pretty(result)
|
||||
.context("Failed to serialize to JSON")?;
|
||||
|
||||
if let Some(path) = output {
|
||||
let mut file = File::create(&path).context("Failed to create output file")?;
|
||||
file.write_all(json.as_bytes())
|
||||
.context("Failed to write to output file")?;
|
||||
} else {
|
||||
println!("{}", json);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn export_json_to_file(result: &AnalysisResult, path: &PathBuf) -> Result<()> {
|
||||
export_json(result, Some(path.clone()))
|
||||
}
|
||||
|
||||
pub fn export_csv(result: &AnalysisResult, _output: &PathBuf) -> Result<()> {
|
||||
println!("Contributors CSV:");
|
||||
println!("Name,Email,Commits,Lines Added,Lines Removed,Net Change");
|
||||
|
||||
for author in &result.contributors.top_contributors {
|
||||
println!(
|
||||
"{},{},{},{},{},{}",
|
||||
author.name,
|
||||
author.email,
|
||||
author.commits,
|
||||
author.lines_added,
|
||||
author.lines_removed,
|
||||
author.net_change
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn export_csv_to_file(result: &AnalysisResult, path: &PathBuf) -> Result<()> {
|
||||
let mut wtr = csv::Writer::from_path(path).context("Failed to create CSV writer")?;
|
||||
|
||||
wtr.write_record(&[
|
||||
"Name", "Email", "Commits", "Lines Added", "Lines Removed", "Net Change",
|
||||
])?;
|
||||
|
||||
for author in &result.contributors.top_contributors {
|
||||
wtr.write_record(&[
|
||||
&author.name,
|
||||
&author.email,
|
||||
&author.commits.to_string(),
|
||||
&author.lines_added.to_string(),
|
||||
&author.lines_removed.to_string(),
|
||||
&author.net_change.to_string(),
|
||||
])?;
|
||||
}
|
||||
|
||||
wtr.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user