use crate::level::Level;
use crate::pipeline::{ConditionalPipelines, parse_conditional_pipeline};
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs;
use std::path::PathBuf;
#[derive(Debug)]
pub struct RunfConfig {
pub level: Level,
pub disabled: HashSet<String>,
pub allowed: Option<HashSet<String>>,
pub data_dir: PathBuf,
pub plugin_dir: PathBuf,
pub home_dir: PathBuf,
pub pipelines: HashMap<String, ConditionalPipelines>,
}
impl RunfConfig {
pub fn resolve() -> Self {
let home_dir = env::var("LOWFAT_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| {
dirs_home().join(".lowfat")
});
let data_dir = env::var("LOWFAT_DATA")
.map(PathBuf::from)
.unwrap_or_else(|_| {
env::var("XDG_DATA_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| dirs_home().join(".local/share"))
.join("lowfat")
});
let plugin_dir = home_dir.join("plugins");
let mut level = Level::Full;
let mut disabled = HashSet::new();
let mut allowed: Option<HashSet<String>> = None;
let mut pipeline_lines: HashMap<String, Vec<(String, String)>> = HashMap::new();
let mut pipelines = HashMap::new();
if let Some(config_path) = find_config() {
if let Ok(content) = fs::read_to_string(&config_path) {
for line in content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some(val) = line.strip_prefix("level=") {
if let Ok(l) = val.parse() {
level = l;
}
} else if let Some(val) = line.strip_prefix("filters=") {
allowed = Some(
val.split(',').map(|s| s.trim().to_string()).collect(),
);
} else if let Some(val) = line.strip_prefix("disable=") {
for name in val.split(',') {
disabled.insert(name.trim().to_string());
}
} else if let Some(rest) = line.strip_prefix("pipeline.") {
if let Some((key, spec)) = rest.split_once('=') {
let key = key.trim();
let spec = spec.trim().to_string();
let (cmd, condition) = match key.split_once('.') {
Some((c, cond)) => (c.to_string(), cond.to_string()),
None => (key.to_string(), String::new()),
};
pipeline_lines
.entry(cmd)
.or_default()
.push((condition, spec));
}
}
}
}
}
for (cmd, lines) in pipeline_lines {
pipelines.insert(cmd, parse_conditional_pipeline(&lines));
}
if let Ok(val) = env::var("LOWFAT_DISABLE") {
for name in val.split(',') {
disabled.insert(name.trim().to_string());
}
}
if let Ok(val) = env::var("LOWFAT_LEVEL") {
if let Ok(l) = val.parse() {
level = l;
}
}
RunfConfig {
level,
disabled,
allowed,
data_dir,
plugin_dir,
home_dir,
pipelines,
}
}
pub fn pipeline_for(&self, cmd: &str) -> Option<&ConditionalPipelines> {
self.pipelines.get(cmd)
}
pub fn is_enabled(&self, name: &str) -> bool {
if self.disabled.contains(name) {
return false;
}
if let Some(ref allowed) = self.allowed {
return allowed.contains(name);
}
true
}
}
pub fn find_config() -> Option<PathBuf> {
let mut dir = env::current_dir().ok()?;
loop {
let candidate = dir.join(".lowfat");
if candidate.is_file() {
return Some(candidate);
}
if !dir.pop() {
return None;
}
}
}
fn dirs_home() -> PathBuf {
env::var("HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from("/tmp"))
}
pub fn find_config_display() -> Option<PathBuf> {
find_config()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_enabled_default() {
let config = RunfConfig {
level: Level::Full,
disabled: HashSet::new(),
allowed: None,
data_dir: PathBuf::new(),
plugin_dir: PathBuf::new(),
home_dir: PathBuf::new(),
pipelines: HashMap::new(),
};
assert!(config.is_enabled("git"));
assert!(config.is_enabled("docker"));
}
#[test]
fn is_enabled_disabled() {
let mut disabled = HashSet::new();
disabled.insert("npm".to_string());
let config = RunfConfig {
level: Level::Full,
disabled,
allowed: None,
data_dir: PathBuf::new(),
plugin_dir: PathBuf::new(),
home_dir: PathBuf::new(),
pipelines: HashMap::new(),
};
assert!(!config.is_enabled("npm"));
assert!(config.is_enabled("git"));
}
#[test]
fn is_enabled_whitelist() {
let mut allowed = HashSet::new();
allowed.insert("git".to_string());
allowed.insert("docker".to_string());
let config = RunfConfig {
level: Level::Full,
disabled: HashSet::new(),
allowed: Some(allowed),
data_dir: PathBuf::new(),
plugin_dir: PathBuf::new(),
home_dir: PathBuf::new(),
pipelines: HashMap::new(),
};
assert!(config.is_enabled("git"));
assert!(!config.is_enabled("npm"));
}
}