diff --git a/devtoolbelt/config.py b/devtoolbelt/config.py new file mode 100644 index 0000000..c7c8bbe --- /dev/null +++ b/devtoolbelt/config.py @@ -0,0 +1,144 @@ +"""Configuration management for Devtoolbelt.""" + +from pathlib import Path +from typing import Any, Dict, Optional + +import yaml + + +class Config: + """Configuration manager for Devtoolbelt.""" + + DEFAULT_CONFIG_NAME = ".devtoolbelt.yml" + CONFIG_DIR = Path.home() / ".config" / "devtoolbelt" + + def __init__(self, config_path: Optional[Path] = None): + """Initialize configuration. + + Args: + config_path: Optional path to config file. If not provided, + uses default locations. + """ + if config_path: + self.config_file = Path(config_path) + else: + self.config_file = self.CONFIG_DIR / self.DEFAULT_CONFIG_NAME + self._config: Dict[str, Any] = {} + + def load(self) -> Dict[str, Any]: + """Load configuration from file. + + Returns: + Dictionary containing configuration. + """ + self._config = self._read_config() + return self._config + + def _read_config(self) -> Dict[str, Any]: + """Read configuration file. + + Returns: + Dictionary containing config values or empty dict. + """ + if not self.config_file.exists(): + return {} + + try: + with open(self.config_file, 'r') as f: + return yaml.safe_load(f) or {} + except (yaml.YAMLError, IOError) as e: + print(f"Warning: Could not read config file: {e}") + return {} + + def save(self, config: Optional[Dict[str, Any]] = None) -> None: + """Save configuration to file. + + Args: + config: Configuration to save. If None, saves current config. + """ + if config: + self._config = config + + self.CONFIG_DIR.mkdir(parents=True, exist_ok=True) + + with open(self.config_file, 'w') as f: + yaml.dump(self._config, f, default_flow_style=False) + + def get(self, key: str, default: Any = None) -> Any: + """Get a configuration value. + + Args: + key: Configuration key (supports dot notation). + default: Default value if key not found. + + Returns: + Configuration value or default. + """ + keys = key.split('.') + value = self._config + + for k in keys: + if isinstance(value, dict): + value = value.get(k) + else: + return default + + if value is None: + return default + + return value + + def set(self, key: str, value: Any) -> None: + """Set a configuration value. + + Args: + key: Configuration key (supports dot notation). + value: Value to set. + """ + keys = key.split('.') + config = self._config + + for k in keys[:-1]: + if k not in config: + config[k] = {} + config = config[k] + + config[keys[-1]] = value + + def get_database_configs(self) -> Dict[str, Dict[str, Any]]: + """Get all database configurations. + + Returns: + Dictionary of database configs by name. + """ + return self.get('databases', {}) + + def get_api_endpoints(self) -> Dict[str, str]: + """Get all API endpoints. + + Returns: + Dictionary of API endpoints by name. + """ + return self.get('api_endpoints', {}) + + def get_default_db(self) -> Optional[str]: + """Get default database name. + + Returns: + Default database name or None. + """ + return self.get('default_database') + + +def get_config(config_path: Optional[str] = None) -> Config: + """Get configuration instance. + + Args: + config_path: Optional path to config file. + + Returns: + Config instance. + """ + config = Config(Path(config_path) if config_path else None) + config.load() + return config