use std::collections::BTreeMap;
use std::fmt;
pub const MAINTENANCE_CONFIG_PATTERN: &str =
r"^(maintenance\.auto|maintenance\.autodetach|gc\.auto|gc\.autodetach)$";
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MaintenanceError {
ConfigNotUtf8,
Start(String),
}
impl fmt::Display for MaintenanceError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ConfigNotUtf8 => {
return formatter.write_str("git config output for maintenance is not UTF-8");
}
Self::Start(reason) => return formatter.write_str(reason.as_str()),
}
}
}
impl std::error::Error for MaintenanceError {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MaintenanceDecision {
Disabled,
Run(Vec<&'static str>),
}
pub fn parse_config_records(output: &[u8]) -> Result<BTreeMap<String, String>, MaintenanceError> {
let text: &str = std::str::from_utf8(output).map_err(|_| return MaintenanceError::ConfigNotUtf8)?;
let mut parsed: BTreeMap<String, String> = BTreeMap::new();
for record in text.split('\0') {
if record.is_empty() {
continue;
}
match record.split_once('\n') {
Some((key, value)) => {
parsed.insert(String::from(key), String::from(value));
}
None => {
parsed.insert(String::from(record), String::from("true"));
}
}
}
return Ok(parsed);
}
fn bool_or_int_true(value: &str) -> bool {
if value == "true" {
return true;
}
if value == "false" {
return false;
}
return javascript_number(value) != 0.0;
}
fn javascript_number(value: &str) -> f64 {
let trimmed: &str = value.trim();
if trimmed.is_empty() {
return 0.0;
}
return trimmed.parse::<f64>().unwrap_or(f64::NAN);
}
pub fn auto_maintenance_arguments(config: &BTreeMap<String, String>) -> MaintenanceDecision {
let maintenance_auto: Option<&String> = config.get("maintenance.auto");
let gc_auto: Option<&String> = config.get("gc.auto");
let enabled: bool = match maintenance_auto {
Some(value) => bool_or_int_true(value.as_str()),
None => match gc_auto {
None => true,
Some(value) => javascript_number(value.as_str()) > 0.0,
},
};
if !enabled {
return MaintenanceDecision::Disabled;
}
let detach_setting: Option<&String> = config
.get("maintenance.autodetach")
.or_else(|| return config.get("gc.autodetach"));
let detach: bool = match detach_setting {
None => true,
Some(value) => bool_or_int_true(value.as_str()),
};
let flag: &'static str = if detach { "--detach" } else { "--no-detach" };
return MaintenanceDecision::Run(vec!["maintenance", "run", "--auto", flag]);
}
#[cfg(test)]
#[path = "maintenance_decision_tests.rs"]
mod tests;