Source code for nostress.utils.config

"""Configuration management."""

import json
import os
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any

from ..exceptions import ConfigurationError


[docs] @dataclass class NostressConfig: """Configuration for nostress CLI.""" # Default key format default_key_format: str = "hex" # Output preferences verbose: bool = False color_output: bool = True # Security settings require_password_confirmation: bool = True min_password_length: int = 8 # File paths default_output_dir: str | None = None
[docs] @classmethod def default(cls) -> "NostressConfig": """Create default configuration. Returns: NostressConfig: Default configuration """ return cls()
[docs] def to_dict(self) -> dict[str, Any]: """Convert to dictionary. Returns: dict: Configuration as dictionary """ return asdict(self)
[docs] @classmethod def from_dict(cls, data: dict[str, Any]) -> "NostressConfig": """Create from dictionary. Args: data: Configuration dictionary Returns: NostressConfig: Configuration instance """ # Filter only valid fields valid_fields = {field.name for field in cls.__dataclass_fields__.values()} filtered_data = {k: v for k, v in data.items() if k in valid_fields} return cls(**filtered_data)
[docs] def get_config_dir() -> Path: """Get configuration directory path. Returns: Path: Configuration directory """ # Use XDG_CONFIG_HOME if available, otherwise ~/.config config_home = os.environ.get("XDG_CONFIG_HOME") if config_home: return Path(config_home) / "nostress" else: return Path.home() / ".config" / "nostress"
[docs] def get_config_file_path() -> Path: """Get configuration file path. Returns: Path: Configuration file path """ return get_config_dir() / "config.json"
[docs] def load_config() -> NostressConfig: """Load configuration from file. Returns: NostressConfig: Loaded configuration or default if file doesn't exist Raises: ConfigurationError: If configuration file is invalid """ config_file = get_config_file_path() if not config_file.exists(): return NostressConfig.default() try: with config_file.open("r") as f: data = json.load(f) return NostressConfig.from_dict(data) except (json.JSONDecodeError, TypeError, ValueError) as e: raise ConfigurationError(f"Invalid configuration file: {e}") from e except OSError as e: raise ConfigurationError(f"Failed to read configuration file: {e}") from e
[docs] def save_config(config: NostressConfig) -> None: """Save configuration to file. Args: config: Configuration to save Raises: ConfigurationError: If saving fails """ config_file = get_config_file_path() try: # Create directory if it doesn't exist config_file.parent.mkdir(parents=True, exist_ok=True) # Write configuration with config_file.open("w") as f: json.dump(config.to_dict(), f, indent=2) except OSError as e: raise ConfigurationError(f"Failed to save configuration: {e}") from e
[docs] def get_setting(key: str, default: Any = None) -> Any: """Get a specific configuration setting. Args: key: Setting key default: Default value if setting not found Returns: Any: Setting value or default """ try: config = load_config() return getattr(config, key, default) except ConfigurationError: return default
[docs] def set_setting(key: str, value: Any) -> None: """Set a specific configuration setting. Args: key: Setting key value: Setting value Raises: ConfigurationError: If setting cannot be saved """ try: config = load_config() setattr(config, key, value) save_config(config) except AttributeError as e: raise ConfigurationError(f"Invalid configuration key: {key}") from e