Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ impl App {
pub fn new(
repo_path: PathBuf,
debug: bool,
config_override: Option<String>,
filter_path: Option<PathBuf>,
start_in_commits: bool,
) -> Result<Self> {
let config = AppConfig::load(debug)?;
let config = AppConfig::load(debug, config_override)?;

// Validate git repo
if !GitCommands::is_valid_repo(&repo_path) {
Expand Down
20 changes: 16 additions & 4 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ impl Default for AppConfig {
}

impl AppConfig {
pub fn load(debug: bool) -> Result<Self> {
pub fn load(debug: bool, config_override: Option<String>) -> Result<Self> {
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
let candidates = config_dir_candidates();
let config_dir = candidates
let mut config_dir = candidates
.iter()
.find(|dir| dir.join("config.yml").exists())
.find(|dir| dir.join("config.yml").exists() || dir.join("config.yaml").exists())
.cloned()
.unwrap_or_else(|| candidates[0].clone());

Expand All @@ -71,7 +71,19 @@ impl AppConfig {
let _ = std::fs::copy(&legacy_state_path, &state_path);
}

let user_config = UserConfig::load(&config_dir)?;
let user_config = UserConfig::load(&config_dir, config_override.as_ref())?;
if let Some(ref over) = config_override {
let over_path = user_config::expand_tilde(over);
if let Some(parent) = over_path.parent() {
if over_path.is_absolute()
|| over.ends_with(".yml")
|| over.ends_with(".yaml")
|| over_path.exists()
{
config_dir = parent.to_path_buf();
}
}
}
let app_state = AppState::load(&state_path)?;

Ok(Self {
Expand Down
76 changes: 74 additions & 2 deletions src/config/user_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,58 @@ impl Default for RefresherConfig {
}
}

pub(crate) fn expand_tilde(path_str: &str) -> std::path::PathBuf {
if let Some(stripped) = path_str.strip_prefix("~/") {
if let Some(home) = dirs::home_dir() {
return home.join(stripped);
}
} else if path_str == "~" {
if let Some(home) = dirs::home_dir() {
return home;
}
}
std::path::PathBuf::from(path_str)
}

impl UserConfig {
pub fn load(config_dir: &Path) -> Result<Self> {
let config_path = config_dir.join("config.yml");
pub fn load(config_dir: &Path, config_override: Option<&String>) -> Result<Self> {
let mut config_path = if config_dir.join("config.yml").exists() {
config_dir.join("config.yml")
} else if config_dir.join("config.yaml").exists() {
config_dir.join("config.yaml")
} else {
config_dir.join("config.yml")
};

if let Some(over) = config_override {
let expanded_path = expand_tilde(over);
if expanded_path.is_absolute()
|| over.ends_with(".yml")
|| over.ends_with(".yaml")
|| expanded_path.exists()
{
config_path = expanded_path;
} else {
let preset_yaml = config_dir.join("presets").join(format!("{}.yaml", over));
let preset_yml = config_dir.join("presets").join(format!("{}.yml", over));
if preset_yaml.exists() {
config_path = preset_yaml;
} else if preset_yml.exists() {
config_path = preset_yml;
} else {
anyhow::bail!(
"Config file or preset not found: '{}' (checked relative, absolute, and presets in {})",
over,
config_dir.display()
);
}
}

if !config_path.exists() {
anyhow::bail!("Config file not found at: {}", config_path.display());
}
}

if config_path.exists() {
let contents = std::fs::read_to_string(&config_path)?;
let config: UserConfig = serde_yaml::from_str(&contents)?;
Expand Down Expand Up @@ -629,3 +678,26 @@ pub struct CustomCommandPrompt {
pub command: Option<String>,
pub filter: Option<String>,
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_expand_tilde() {
let expanded = expand_tilde("~/test/path");
if let Some(home) = dirs::home_dir() {
assert_eq!(expanded, home.join("test/path"));
}
}

#[test]
fn test_load_nonexistent_override_fails() {
let temp_dir = std::env::temp_dir();
let result = UserConfig::load(
&temp_dir,
Some(&"/path/that/does/not/exist/definitely/config.yml".to_string()),
);
assert!(result.is_err());
}
}
41 changes: 40 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ struct Cli {
#[arg(short = 'f', long = "filter", value_name = "PATH")]
filter_path: Option<PathBuf>,

/// Print the default configuration YAML to stdout and exit
#[arg(long)]
print_default_config: bool,

/// Configuration file or preset name to use (e.g. 'popup' or ~/.config/lazygitrs/config.yml)
#[arg(short = 'c', long = "config")]
config: Option<String>,

/// Launch directly in the commits panel
#[arg(long)]
commits: bool,
Expand Down Expand Up @@ -106,6 +114,13 @@ fn main() {
return;
}

if cli.print_default_config {
let config = config::user_config::UserConfig::default();
let yaml = serde_yaml::to_string(&config).unwrap();
println!("{}", yaml);
std::process::exit(0);
}

// Set up logging if debug mode
if cli.debug {
tracing_subscriber::fmt()
Expand All @@ -119,7 +134,13 @@ fn main() {
.or(cli.work_tree)
.unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));

match app::App::new(repo_path, cli.debug, cli.filter_path, cli.commits) {
match app::App::new(
repo_path,
cli.debug,
cli.config,
cli.filter_path,
cli.commits,
) {
Ok(app) => {
if let Err(e) = app.run() {
eprintln!("Error: {:#}", e);
Expand All @@ -145,4 +166,22 @@ mod tests {
let cli_default = Cli::parse_from(["lazygitrs"]);
assert!(!cli_default.commits);
}

#[test]
fn test_cli_config_flag() {
let cli = Cli::parse_from(["lazygitrs", "-c", "my-config.yml"]);
assert_eq!(cli.config.as_deref(), Some("my-config.yml"));

let cli_long = Cli::parse_from(["lazygitrs", "--config", "popup"]);
assert_eq!(cli_long.config.as_deref(), Some("popup"));

let cli_default = Cli::parse_from(["lazygitrs"]);
assert_eq!(cli_default.config, None);
}

#[test]
fn test_cli_print_default_config_flag() {
let cli = Cli::parse_from(["lazygitrs", "--print-default-config"]);
assert!(cli.print_default_config);
}
}
Loading