#!/usr/bin/env python3 from typing import Any, Optional from tomllib import load from pathlib import Path from dataclasses import dataclass, field import questionary TEMPLATES_DIR = Path("./_templates") @dataclass(frozen=True) class TemplateParameter: prompt: str default: Optional[Any] = None @dataclass class ProjectTemplate: name: str path: Path templates: list[str] = field(default_factory=list) parameters: dict[str, TemplateParameter] = field(default_factory=dict) templates: dict[str, ProjectTemplate] = {} for v in TEMPLATES_DIR.rglob("template.toml"): with v.open("rb") as fp: data = load(fp) tpl = ProjectTemplate( data.get('name', str(v.parent.relative_to(TEMPLATES_DIR))), v.parent, data.get('templates', []), { k: TemplateParameter(**v) for k, v in data.get('params', {}).items() } ) templates[tpl.name] = tpl project_name: Optional[str] = questionary.text("Project name").ask() if not project_name: exit(1) template: Optional[ProjectTemplate] = questionary.select("Template", [ questionary.Choice(tpl.name, tpl) for tpl in templates.values() ]).ask() if not template: exit(1) parameters = questionary.prompt([ { "type": "text", "name": k, "message": param.prompt, "default": param.default } for k, param in template.parameters.items() ]) if not parameters: exit(1) project_dir = Path(project_name) project_dir.mkdir(parents=True, exist_ok=True) for base, dirs, files in template.path.walk(): for dirname in dirs: (base / dirname).mkdir(exist_ok=True, parents=True) for filename in files: in_path = base / filename file_name = str(in_path.relative_to(template.path)) out_path = project_dir / file_name if file_name == "template.toml": continue elif file_name in template.templates: print("TMPL", out_path) with in_path.open("r") as fp_in: with out_path.open("w") as fp_out: for line in fp_in: line = line.replace("__PROGNAME", project_name.split("/")[-1]) line = line.replace("__PROJECT", project_name) for param in template.parameters: line = line.replace(f"__{param}", parameters[param]) fp_out.write(line) else: print("COPY", out_path) with in_path.open("rb") as fp_in: with out_path.open("wb") as fp_out: while (chunk := fp_in.read(32768)): fp_out.write(chunk)