use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Parse error: {0}")]
ParseError(#[from] toml::de::Error),
#[error("Serialize error: {0}")]
SerializeError(#[from] toml::ser::Error),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub rust_bucket_version: String,
pub test_timeout: u32,
pub project_name: String,
}
impl Default for Config {
fn default() -> Self {
Self {
rust_bucket_version: env!("CARGO_PKG_VERSION").to_string(),
test_timeout: 120,
project_name: env!("CARGO_PKG_NAME").to_string(),
}
}
}
impl Config {
pub fn load(path: &Path) -> Result<Config, ConfigError> {
let contents = fs::read_to_string(path)?;
let config = toml::from_str(&contents)?;
Ok(config)
}
pub fn save(&self, path: &Path) -> Result<(), ConfigError> {
let toml_string = toml::to_string_pretty(self)?;
let version = env!("CARGO_PKG_VERSION");
let header = format!(
"# Generated by rust-bucket v{}. DO NOT EDIT BY HAND.\n\n",
version
);
let contents = header + &toml_string;
fs::write(path, contents)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_default_config() {
let config = Config::default();
assert_eq!(config.rust_bucket_version, env!("CARGO_PKG_VERSION"));
assert_eq!(config.test_timeout, 120);
assert_eq!(config.project_name, env!("CARGO_PKG_NAME"));
}
#[test]
fn test_save_and_load_roundtrip() {
let temp_dir = tempfile::tempdir().unwrap();
let config_path = temp_dir.path().join("rust-bucket.toml");
let original_config = Config {
rust_bucket_version: "0.1.0".to_string(),
test_timeout: 300,
project_name: "test-project".to_string(),
};
original_config.save(&config_path).unwrap();
let file_contents = fs::read_to_string(&config_path).unwrap();
assert!(file_contents.starts_with("# Generated by rust-bucket v"));
assert!(file_contents.contains("DO NOT EDIT BY HAND"));
let loaded_config = Config::load(&config_path).unwrap();
assert_eq!(loaded_config.rust_bucket_version, "0.1.0");
assert_eq!(loaded_config.test_timeout, 300);
assert_eq!(loaded_config.project_name, "test-project");
}
#[test]
fn test_load_nonexistent_file() {
let path = PathBuf::from("/nonexistent/path/config.toml");
let result = Config::load(&path);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ConfigError::IoError(_)));
}
#[test]
fn test_load_invalid_toml() {
let temp_dir = tempfile::tempdir().unwrap();
let config_path = temp_dir.path().join("invalid.toml");
fs::write(&config_path, "this is not valid toml { [ }").unwrap();
let result = Config::load(&config_path);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ConfigError::ParseError(_)));
}
#[test]
fn test_save_to_readonly_directory() {
}
}