Files
patternforge/src/patternforge/template.py
7000pctAUTO 08b59ff9a6
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / build (push) Has been cancelled
Initial upload: PatternForge CLI tool with pattern detection and boilerplate generation
2026-02-02 22:26:00 +00:00

234 lines
7.1 KiB
Python

import json
from datetime import datetime
from pathlib import Path
from typing import Any
import yaml
from jinja2 import Environment, FileSystemLoader, TemplateSyntaxError
from patternforge.config import Config
class TemplateManager:
def __init__(self, config: Config) -> None:
self.config = config
self._ensure_templates_dir()
def _ensure_templates_dir(self) -> None:
self.config.templates_dir.mkdir(parents=True, exist_ok=True)
def _get_template_path(self, name: str) -> Path:
return self.config.templates_dir / f"{name}.yaml"
def _get_pattern_path(self, name: str) -> Path:
return self.config.patterns_dir / f"{name}.yaml"
def create_template(
self,
name: str,
pattern_file: str,
custom_template: str | None = None,
description: str = "",
) -> None:
pattern_path = Path(pattern_file)
if not pattern_path.exists():
raise FileNotFoundError(f"Pattern file not found: {pattern_file}")
with open(pattern_path) as f:
patterns = yaml.safe_load(f)
template_content = ""
if custom_template:
with open(custom_template) as f:
template_content = f.read()
else:
template_content = self._generate_default_template(patterns)
template_data = {
"name": name,
"description": description,
"created": datetime.now().isoformat(),
"language": patterns.get("language", "unknown"),
"patterns": patterns,
"template": template_content,
}
template_path = self._get_template_path(name)
with open(template_path, "w") as f:
yaml.dump(template_data, f, default_flow_style=False)
def _generate_default_template(self, patterns: dict[str, Any]) -> str:
language = patterns.get("language", "python")
if language in ["python"]:
return '''# Generated by PatternForge
# Based on analyzed patterns from {{ project_name }}
{% if class_name %}
class {{ class_name }}:
"""{{ class_docstring }}"""
def __init__(self{% if init_params %}, {{ init_params }}{% endif %}):
{% for field in fields %}
self.{{ field }} = {{ field }}
{% endfor %}
{%- if methods %}
{% for method in methods %}
def {{ method.name }}(self{% if method.params %}, {{ method.params }}{% endif %}):
"""{{ method.docstring }}"""
pass
{% endfor %}
{%- endif %}
{% elif function_name %}
def {{ function_name }}({% if params %}{{ params }}{% endif %}):
"""{{ docstring }}"""
pass
{% else %}
# {{ entity_name }} - generated boilerplate
{% endif %}
'''
elif language in ["javascript", "typescript"]:
return '''// Generated by PatternForge
{% if class_name %}
export class {{ class_name }} {
{% for field in fields %}
private {{ field }};
{% endfor %}
constructor({% if params %}{{ params }}{% endif %}) {
{% for field in fields %}
this.{{ field }} = {{ field }};
{% endfor %}
}
{% if methods %}
{% for method in methods %}
{{ method.name }}({% if method.params %}{{ method.params }}{% endif %}) {
// {{ method.docstring }}
}
{% endfor %}
{%- endif %}
}
{% elif function_name %}
export function {{ function_name }}({% if params %}{{ params }}{% endif %}) {
// {{ docstring }}
}
{% else %}
// {{ entity_name }} - generated boilerplate
{% endif %}
'''
else:
return '''// Generated by PatternForge
// {{ entity_name }}
{% if class_name %}
class {{ class_name }} {
{% for field in fields %}
{{ field }};
{% endfor %}
constructor({% if params %}{{ params }}{% endif %}) {
{% for field in fields %}
this.{{ field }} = {{ field }};
{% endfor %}
}
}
{% else %}
// {{ entity_name }}
{% endif %}
'''
def list_templates(self) -> list[dict[str, Any]]:
templates: list[dict[str, Any]] = []
if not self.config.templates_dir.exists():
return templates
for f in self.config.templates_dir.glob("*.yaml"):
with open(f) as fp:
data = yaml.safe_load(fp)
if data:
templates.append({
"name": data.get("name", f.stem),
"description": data.get("description", ""),
"created": data.get("created", ""),
"language": data.get("language", ""),
})
return templates
def get_template(self, name: str) -> dict[str, Any] | None:
path = self._get_template_path(name)
if not path.exists():
return None
with open(path) as f:
return yaml.safe_load(f)
def remove_template(self, name: str) -> bool:
path = self._get_template_path(name)
if path.exists():
path.unlink()
return True
return False
def render_template(
self,
name: str,
context: dict[str, Any],
) -> str:
template_data = self.get_template(name)
if not template_data:
raise ValueError(f"Template not found: {name}")
template_str = template_data.get("template", "")
try:
env = Environment(
loader=FileSystemLoader(str(self.config.templates_dir)),
trim_blocks=True,
lstrip_blocks=True,
)
template = env.from_string(template_str)
return template.render(**context)
except TemplateSyntaxError as e:
raise ValueError(f"Template syntax error: {e}")
def export_patterns(
self,
source: str,
destination: str,
format: str = "yaml",
) -> None:
src = Path(source)
dst = Path(destination)
dst.parent.mkdir(parents=True, exist_ok=True)
if src.is_file():
with open(src) as f:
data = yaml.safe_load(f)
if format == "json":
with open(dst, "w") as f:
json.dump(data, f, indent=2)
else:
with open(dst, "w") as f:
yaml.dump(data, f, default_flow_style=False)
else:
for f in src.glob("*.yaml"):
with open(f) as fp:
data = yaml.safe_load(fp)
out_path = dst / f.name
if format == "json":
with open(out_path.with_suffix(".json"), "w") as fp:
json.dump(data, fp, indent=2)
else:
with open(out_path, "w") as fp:
yaml.dump(data, fp, default_flow_style=False)
def import_patterns(self, source: str) -> None:
src = Path(source)
if src.is_file():
name = src.stem
import_path = self._get_pattern_path(name)
import_path.parent.mkdir(parents=True, exist_ok=True)
import_path.write_bytes(src.read_bytes())
else:
for f in src.glob("*.yaml"):
import_path = self._get_pattern_path(f.stem)
import_path.parent.mkdir(parents=True, exist_ok=True)
import_path.write_bytes(f.read_bytes())