use std::time::Duration;
use git2::Repository;
use crate::error::{ConfigError, Result};
pub struct WorkonConfig<'repo> {
repo: &'repo Repository,
}
impl<'repo> WorkonConfig<'repo> {
pub fn new(repo: &'repo Repository) -> Result<Self> {
Ok(Self { repo })
}
pub fn default_branch(&self, cli_override: Option<&str>) -> Result<Option<String>> {
if let Some(override_val) = cli_override {
return Ok(Some(override_val.to_string()));
}
let config = self.repo.config()?;
match config.get_string("workon.defaultBranch") {
Ok(val) => Ok(Some(val)),
Err(_) => Ok(None), }
}
pub fn pr_format(&self, cli_override: Option<&str>) -> Result<String> {
let format = if let Some(override_val) = cli_override {
override_val.to_string()
} else {
let config = self.repo.config()?;
config
.get_string("workon.prFormat")
.unwrap_or_else(|_| "pr-{number}".to_string())
};
if !format.contains("{number}") {
return Err(ConfigError::InvalidPrFormat {
format: format.clone(),
reason: "Format must contain {number} placeholder".to_string(),
}
.into());
}
let valid_placeholders = ["{number}", "{title}", "{author}", "{branch}"];
let mut remaining = format.clone();
for placeholder in &valid_placeholders {
remaining = remaining.replace(placeholder, "");
}
if remaining.contains('{') {
return Err(ConfigError::InvalidPrFormat {
format: format.clone(),
reason: format!(
"Invalid placeholder found. Valid placeholders: {}",
valid_placeholders.join(", ")
),
}
.into());
}
Ok(format)
}
pub fn post_create_hooks(&self) -> Result<Vec<String>> {
self.read_multivar("workon.postCreateHook")
}
pub fn copy_patterns(&self) -> Result<Vec<String>> {
self.read_multivar("workon.copyPattern")
}
pub fn copy_excludes(&self) -> Result<Vec<String>> {
self.read_multivar("workon.copyExclude")
}
pub fn copy_include_ignored(&self, cli_override: Option<bool>) -> Result<bool> {
if let Some(override_val) = cli_override {
return Ok(override_val);
}
let config = self.repo.config()?;
match config.get_bool("workon.copyIncludeIgnored") {
Ok(val) => Ok(val),
Err(_) => Ok(false),
}
}
pub fn auto_copy_untracked(&self, cli_override: Option<bool>) -> Result<bool> {
if let Some(override_val) = cli_override {
return Ok(override_val);
}
let config = self.repo.config()?;
match config.get_bool("workon.autoCopyUntracked") {
Ok(val) => Ok(val),
Err(_) => Ok(false), }
}
pub fn prune_protected_branches(&self) -> Result<Vec<String>> {
self.read_multivar("workon.pruneProtectedBranches")
}
pub fn is_protected(&self, branch_name: &str) -> bool {
let patterns = match self.prune_protected_branches() {
Ok(p) => p,
Err(_) => return false,
};
for pattern in patterns {
if pattern == branch_name {
return true;
}
if pattern == "*" {
return true;
}
if let Some(prefix) = pattern.strip_suffix("/*") {
if branch_name.starts_with(&format!("{}/", prefix)) {
return true;
}
}
}
false
}
pub fn hook_timeout(&self) -> Result<Duration> {
let config = self.repo.config()?;
let seconds = match config.get_i64("workon.hookTimeout") {
Ok(val) => val.max(0) as u64,
Err(_) => 300,
};
Ok(Duration::from_secs(seconds))
}
fn read_multivar(&self, key: &str) -> Result<Vec<String>> {
let config = self.repo.config()?;
let mut values = Vec::new();
if let Ok(mut entries) = config.multivar(key, None) {
while let Some(entry) = entries.next() {
let entry = entry?;
if let Some(value) = entry.value() {
values.push(value.to_string());
}
}
}
Ok(values)
}
}