Source code for pjkm.core.models.project

"""Project configuration and archetype definitions."""

from __future__ import annotations

import re
from enum import StrEnum
from pathlib import Path

from pydantic import BaseModel, Field, computed_field, field_validator


[docs] class Archetype(StrEnum): """Supported project archetypes."""
[docs] SINGLE_PACKAGE = "single_package"
[docs] SERVICE = "service"
[docs] POLY_REPO = "poly_repo"
[docs] SCRIPT_TOOL = "script_tool"
[docs] class ProjectConfig(BaseModel): """Central configuration describing what the user wants to build."""
[docs] project_name: str = Field(min_length=1, max_length=100)
[docs] archetype: Archetype
[docs] python_version: str = Field(default="3.13")
[docs] author_name: str = Field(default="")
[docs] author_email: str = Field(default="")
[docs] license: str = Field(default="MIT")
[docs] description: str = Field(default="")
[docs] selected_groups: list[str] = Field(default_factory=list)
[docs] target_dir: Path = Field(default_factory=lambda: Path.cwd())
[docs] dry_run: bool = False
[docs] template_overrides: dict[str, str] = Field(default_factory=dict)
@computed_field # type: ignore[prop-decorator] @property
[docs] def project_slug(self) -> str: """Normalized project name for use in Python package paths.""" return re.sub(r"[^a-zA-Z0-9]", "_", self.project_name).lower().strip("_")
@computed_field # type: ignore[prop-decorator] @property
[docs] def project_dir(self) -> Path: """Full path to the project directory that will be created.""" return self.target_dir / self.project_name
@field_validator("python_version") @classmethod
[docs] def validate_python_version(cls, v: str) -> str: if not re.match(r"^\d+\.\d+$", v): msg = f"Python version must be in X.Y format, got {v!r}" raise ValueError(msg) return v