"""Configuration handling for Project Scaffold CLI.""" from pathlib import Path from typing import Any, Dict, List, Optional import yaml class Config: """Configuration management for project scaffold CLI.""" def __init__( self, author: Optional[str] = None, email: Optional[str] = None, license: Optional[str] = None, description: Optional[str] = None, default_language: Optional[str] = None, default_ci: Optional[str] = None, default_template: Optional[str] = None, template_vars: Optional[Dict[str, Any]] = None, custom_templates_dir: Optional[str] = None, config_path: Optional[str] = None, ): self.author = author self.email = email self.license = license self.description = description self.default_language = default_language self.default_ci = default_ci self.default_template = default_template self.template_vars = template_vars or {} self.custom_templates_dir = custom_templates_dir self.config_path = config_path @classmethod def load(cls, config_path: Optional[str] = None) -> "Config": """Load configuration from file.""" if config_path: config_file = Path(config_path) if config_file.exists(): return cls._from_file(config_file) search_paths = [ Path("project.yaml"), Path(".project-scaffoldrc"), Path.cwd() / "project.yaml", Path.cwd() / ".project-scaffoldrc", ] for path in search_paths: if path.exists(): return cls._from_file(path) home_config = Path.home() / ".config" / "project-scaffold" / "config.yaml" if home_config.exists(): return cls._from_file(home_config) return cls() @classmethod def _from_file(cls, path: Path) -> "Config": """Load configuration from a YAML file.""" try: with open(path, "r", encoding="utf-8") as f: data = yaml.safe_load(f) or {} except yaml.YAMLError as e: raise ValueError(f"Invalid YAML in {path}: {e}") project_config = data.get("project", {}) defaults = data.get("defaults", {}) template_vars = data.get("template_vars", {}) custom_templates_dir = data.get("custom_templates_dir") return cls( author=project_config.get("author"), email=project_config.get("email"), license=project_config.get("license"), description=project_config.get("description"), default_language=defaults.get("language"), default_ci=defaults.get("ci"), default_template=defaults.get("template"), template_vars=template_vars, custom_templates_dir=custom_templates_dir, config_path=str(path), ) def save(self, path: Path) -> None: """Save configuration to a YAML file.""" data = { "project": { "author": self.author, "email": self.email, "license": self.license, "description": self.description, }, "defaults": { "language": self.default_language, "ci": self.default_ci, "template": self.default_template, }, "template_vars": self.template_vars, } if self.custom_templates_dir: data["custom_templates_dir"] = self.custom_templates_dir with open(path, "w", encoding="utf-8") as f: yaml.dump(data, f, default_flow_style=False, sort_keys=False) def get_template_dirs(self) -> List[str]: """Get list of template directories to search.""" dirs = [] if self.custom_templates_dir: dirs.append(self.custom_templates_dir) home_templates = ( Path.home() / ".local" / "share" / "project-scaffold" / "templates" ) dirs.append(str(home_templates)) return dirs def get_custom_templates_dir(self) -> str: """Get the directory for custom templates.""" if self.custom_templates_dir: return self.custom_templates_dir custom_dir = ( Path.home() / ".config" / "project-scaffold" / "templates" ) custom_dir.mkdir(parents=True, exist_ok=True) return str(custom_dir) def get_template_vars(self, language: str) -> Dict[str, Any]: """Get template variables for a specific language.""" return self.template_vars.get(language, {}) @property def ci(self) -> Optional[str]: """Get default CI provider.""" return self.default_ci @property def template(self) -> Optional[str]: """Get default template.""" return self.default_template