use std::fs;
use std::path::{Path, PathBuf};
use thiserror::Error;
#[derive(Error, Debug)]
#[allow(clippy::enum_variant_names)]
pub enum SetupError {
#[error("Failed to determine config directory: {0}")]
ConfigDirError(String),
#[error("Failed to create directory {path}: {source}")]
CreateDirError {
path: PathBuf,
source: std::io::Error,
},
#[error("Failed to write file {path}: {source}")]
WriteFileError {
path: PathBuf,
source: std::io::Error,
},
#[error("Failed to set file permissions for {path}: {source}")]
PermissionError {
path: PathBuf,
source: std::io::Error,
},
}
pub fn get_config_dir() -> Result<PathBuf, SetupError> {
if cfg!(unix) {
if let Some(mut home_path) = dirs::home_dir() {
home_path.push(".config");
home_path.push("quetty");
return Ok(home_path);
}
}
dirs::config_dir()
.map(|mut path| {
path.push("quetty");
path
})
.ok_or_else(|| {
SetupError::ConfigDirError("Unable to determine config directory".to_string())
})
}
pub fn get_config_file_path() -> Result<PathBuf, SetupError> {
let mut config_dir = get_config_dir()?;
config_dir.push("config.toml");
Ok(config_dir)
}
pub fn get_themes_dir() -> Result<PathBuf, SetupError> {
let mut config_dir = get_config_dir()?;
config_dir.push("themes");
Ok(config_dir)
}
pub fn is_config_initialized() -> bool {
match get_config_dir() {
Ok(config_dir) => {
let profiles_dir = config_dir.join("profiles");
let default_profile_dir = profiles_dir.join("default");
let default_env = default_profile_dir.join(".env");
profiles_dir.exists() && default_profile_dir.exists() && default_env.exists()
}
Err(_) => false,
}
}
pub fn initialize_config_dir() -> Result<PathBuf, SetupError> {
let config_dir = get_config_dir()?;
let themes_dir = get_themes_dir()?;
create_dir_if_not_exists(&config_dir)?;
create_dir_if_not_exists(&themes_dir)?;
let profiles_dir = config_dir.join("profiles");
create_dir_if_not_exists(&profiles_dir)?;
let default_profile_dir = profiles_dir.join("default");
create_dir_if_not_exists(&default_profile_dir)?;
let env_file = default_profile_dir.join(".env");
if !env_file.exists() {
let env_content = "# Environment variables for default profile\n# SECRETS AND AUTHENTICATION ONLY\n# For other settings, create config.toml or keys.toml in this directory\n\n# Add your Azure credentials here\n# AZURE_AD__TENANT_ID=your-tenant-id\n# AZURE_AD__CLIENT_ID=your-client-id\n# AZURE_AD__CLIENT_SECRET=your-client-secret\n";
write_file_with_permissions(&env_file, env_content)?;
log::info!("Created .env file: {}", env_file.display());
}
log::info!(
"Profile directory structure created: {}",
profiles_dir.display()
);
log::info!("Config directory initialized: {}", config_dir.display());
Ok(config_dir)
}
fn create_dir_if_not_exists(path: &Path) -> Result<(), SetupError> {
if !path.exists() {
fs::create_dir_all(path).map_err(|source| SetupError::CreateDirError {
path: path.to_path_buf(),
source,
})?;
}
Ok(())
}
fn write_file_with_permissions(path: &Path, content: &str) -> Result<(), SetupError> {
fs::write(path, content).map_err(|source| SetupError::WriteFileError {
path: path.to_path_buf(),
source,
})?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let permissions = fs::Permissions::from_mode(0o600); fs::set_permissions(path, permissions).map_err(|source| SetupError::PermissionError {
path: path.to_path_buf(),
source,
})?;
}
Ok(())
}
pub fn find_config_file() -> Option<PathBuf> {
match get_config_file_path() {
Ok(standard_config) if standard_config.exists() => Some(standard_config),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_dir_determination() {
let result = get_config_dir();
assert!(result.is_ok());
let config_dir = result.unwrap();
assert!(config_dir.to_string_lossy().contains("quetty"));
}
#[test]
fn test_find_config_file_standard_location() {
let result = find_config_file();
assert!(result.is_some() || result.is_none());
}
}