From 9562b3dd6364d8d9d87a26d9ed38ca1571f3e27a Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Wed, 4 Feb 2026 15:45:25 +0000 Subject: [PATCH] Initial upload: GitPulse - Developer Productivity Analyzer CLI tool --- src/commands/export.rs | 158 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/commands/export.rs diff --git a/src/commands/export.rs b/src/commands/export.rs new file mode 100644 index 0000000..bb10041 --- /dev/null +++ b/src/commands/export.rs @@ -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, + + #[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, + 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 "); + } + } + _ => { + 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) -> 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(()) + } +}