Initial upload: GitPulse - Developer Productivity Analyzer CLI tool
Some checks failed
CI / test (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-02-04 15:45:39 +00:00
parent 78fce8dcd9
commit 3d635d662f

152
src/config/file.rs Normal file
View File

@@ -0,0 +1,152 @@
use anyhow::{Context, Result};
use home::home_dir;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisConfig {
pub default_time_period: String,
pub max_contributors: usize,
pub include_merges: bool,
pub refactoring_detection: bool,
}
impl Default for AnalysisConfig {
fn default() -> Self {
Self {
default_time_period: "30 days".to_string(),
max_contributors: 50,
include_merges: false,
refactoring_detection: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisplayConfig {
pub theme: String,
pub chart_height: u16,
pub compact_tables: bool,
pub show_sparklines: bool,
}
impl Default for DisplayConfig {
fn default() -> Self {
Self {
theme: "dark".to_string(),
chart_height: 10,
compact_tables: false,
show_sparklines: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportConfig {
pub default_format: String,
pub include_timestamps: bool,
pub indent_json: bool,
}
impl Default for ExportConfig {
fn default() -> Self {
Self {
default_format: "json".to_string(),
include_timestamps: true,
indent_json: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColorsConfig {
pub primary: String,
pub secondary: String,
pub accent: String,
pub error: String,
pub background: String,
}
impl Default for ColorsConfig {
fn default() -> Self {
Self {
primary: "blue".to_string(),
secondary: "green".to_string(),
accent: "yellow".to_string(),
error: "red".to_string(),
background: "black".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub analysis: AnalysisConfig,
pub display: DisplayConfig,
pub export: ExportConfig,
pub colors: ColorsConfig,
}
impl Default for Config {
fn default() -> Self {
Self {
analysis: AnalysisConfig::default(),
display: DisplayConfig::default(),
export: ExportConfig::default(),
colors: ColorsConfig::default(),
}
}
}
impl Config {
pub fn config_path() -> Result<PathBuf> {
let home = home_dir().ok_or_else(|| anyhow::anyhow!("Could not find home directory"))?;
let config_dir = home.join(".config").join("gitpulse");
Ok(config_dir.join("config.toml"))
}
pub fn load(custom_path: Option<&PathBuf>) -> Result<Self> {
let config_path = if let Some(path) = custom_path {
path.clone()
} else {
Self::config_path()?
};
if !config_path.exists() {
return Ok(Self::default());
}
let content = fs::read_to_string(&config_path)
.with_context(|| format!("Failed to read config from: {}", config_path.display()))?;
let config: Config = toml::from_str(&content)
.with_context(|| format!("Failed to parse config: {}", config_path.display()))?;
Ok(config)
}
pub fn save(&self) -> Result<()> {
let config_path = Self::config_path()?;
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create config directory: {}", parent.display()))?;
}
let content = toml::to_string_pretty(self)
.context("Failed to serialize config")?;
fs::write(&config_path, content)
.with_context(|| format!("Failed to write config to: {}", config_path.display()))?;
Ok(())
}
pub fn merge(&mut self, other: Config) {
self.analysis = other.analysis;
self.display = other.display;
self.export = other.export;
self.colors = other.colors;
}
}