11"""
22Template engine wrapper for code generation.
33
4- Provides a simple interface for Jinja2 template rendering
4+ Provides a clean, language-agnostic interface for Jinja2 template rendering
55with common utilities for code generation.
66"""
77
8- import os
98from typing import Dict , Any , Optional
109from pathlib import Path
10+ from .naming import NameSanitizer
1111
1212try :
1313 from jinja2 import (
1414 Environment ,
1515 FileSystemLoader ,
16- BaseLoader ,
1716 DictLoader ,
1817 select_autoescape ,
1918 )
@@ -33,7 +32,7 @@ class TemplateError(Exception):
3332
3433
3534class TemplateEngine :
36- """Wrapper for Jinja2 template engine with code generation utilities ."""
35+ """Language-agnostic wrapper for Jinja2 template engine."""
3736
3837 def __init__ (self , template_dir : Optional [Path ] = None ):
3938 """
@@ -42,6 +41,8 @@ def __init__(self, template_dir: Optional[Path] = None):
4241 Args:
4342 template_dir: Directory containing template files
4443 """
44+ self ._name_sanitizer = NameSanitizer ()
45+
4546 if Environment is None :
4647 raise TemplateError (
4748 "Jinja2 is required for template functionality. "
@@ -57,17 +58,17 @@ def _setup_environment(self):
5758 if self .template_dir and self .template_dir .exists ():
5859 loader = FileSystemLoader (str (self .template_dir ))
5960 else :
60- # Use in-memory templates
61+ # Use in-memory templates as fallback
6162 loader = DictLoader ({})
6263
6364 self ._env = Environment (
6465 loader = loader ,
6566 autoescape = select_autoescape (["html" , "xml" ]),
66- # trim_blocks=True,
6767 lstrip_blocks = True ,
68+ # trim_blocks=True,
6869 )
6970
70- # Add custom filters for code generation
71+ # Add language-agnostic filters for code generation
7172 self ._env .filters ["snake_case" ] = self ._snake_case_filter
7273 self ._env .filters ["camel_case" ] = self ._camel_case_filter
7374 self ._env .filters ["pascal_case" ] = self ._pascal_case_filter
@@ -79,7 +80,7 @@ def render_template(self, template_name: str, context: Dict[str, Any]) -> str:
7980 Render a template with the given context.
8081
8182 Args:
82- template_name: Name of template file
83+ template_name: Name of template file (e.g., 'struct.go.j2')
8384 context: Variables to pass to template
8485
8586 Returns:
@@ -122,31 +123,31 @@ def add_template(self, name: str, content: str):
122123
123124 self ._env .loader .mapping [name ] = content
124125
125- # Template filters for code generation
126+ def template_exists (self , template_name : str ) -> bool :
127+ """Check if a template exists."""
128+ try :
129+ self ._env .get_template (template_name )
130+ return True
131+ except :
132+ return False
126133
127- def _snake_case_filter (self , value : str ) -> str :
128- """Convert string to snake_case."""
129- import re
134+ def list_templates (self ) -> list [str ]:
135+ """List all available templates."""
136+ try :
137+ return self ._env .list_templates ()
138+ except :
139+ return []
140+
141+ # Language-agnostic template filters
130142
131- # Insert underscore before uppercase letters
132- s1 = re .sub ("([a-z0-9])([A-Z])" , r"\1_\2" , str (value ))
133- # Replace spaces and hyphens with underscores
134- s2 = re .sub (r"[-\s]+" , "_" , s1 )
135- return s2 .lower ()
143+ def _snake_case_filter (self , value : str ) -> str :
144+ return self ._name_sanitizer ._to_snake_case (value )
136145
137146 def _camel_case_filter (self , value : str ) -> str :
138- """Convert string to camelCase."""
139- snake = self ._snake_case_filter (value )
140- parts = snake .split ("_" )
141- if not parts :
142- return str (value )
143- return parts [0 ].lower () + "" .join (p .capitalize () for p in parts [1 :])
147+ return self ._name_sanitizer ._to_camel_case (value )
144148
145149 def _pascal_case_filter (self , value : str ) -> str :
146- """Convert string to PascalCase."""
147- snake = self ._snake_case_filter (value )
148- parts = snake .split ("_" )
149- return "" .join (p .capitalize () for p in parts if p )
150+ return self ._name_sanitizer ._to_pascal_case (value )
150151
151152 def _indent_filter (self , value : str , spaces : int = 4 ) -> str :
152153 """Indent all lines in a string."""
@@ -160,61 +161,14 @@ def _comment_filter(self, value: str, style: str = "//") -> str:
160161 return "\n " .join (f"{ style } { line } " if line .strip () else line for line in lines )
161162
162163
163- # Built-in templates for common patterns
164- GO_STRUCT_TEMPLATE = """
165- {%- if description %}
166- // {{ description }}
167- {%- endif %}
168- type {{ struct_name }} struct {
169- {%- for field in fields %}
170- {%- if field.comment %}
171- // {{ field.comment }}
172- {%- endif %}
173- {{ field.name }} {{ field.type }} {% if field.json_tag %} {{ field.json_tag }} {% endif %}
174- {%- endfor %}
175- }
176- """
177-
178- PYTHON_DATACLASS_TEMPLATE = """
179- @dataclass
180- class {{ class_name }}:
181- {%- for field in fields %}
182- {{ field.name }}: {{ field.type }}{% if field.optional %} = None{% endif %}
183- {%- endfor %}
184- """
185-
186- # Default template engine instance
187- _default_engine = None
188-
189-
190- def get_default_template_engine () -> TemplateEngine :
191- """Get the default template engine instance."""
192- global _default_engine
193- if _default_engine is None :
194- _default_engine = TemplateEngine ()
195-
196- # Add built-in templates
197- _default_engine .add_template ("go_struct" , GO_STRUCT_TEMPLATE )
198- _default_engine .add_template ("python_dataclass" , PYTHON_DATACLASS_TEMPLATE )
199-
200- return _default_engine
201-
202-
203- def render_go_struct (
204- struct_name : str , fields : list , context : Dict [str , Any ] = None
205- ) -> str :
164+ def create_template_engine (template_dir : Optional [Path ] = None ) -> TemplateEngine :
206165 """
207- Convenience function to render a Go struct .
166+ Create a template engine instance .
208167
209168 Args:
210- struct_name: Name of the struct
211- fields: List of field dictionaries
212- context: Additional context variables
169+ template_dir: Directory containing template files
213170
214171 Returns:
215- Rendered Go struct code
172+ Configured TemplateEngine instance
216173 """
217- engine = get_default_template_engine ()
218- template_context = {"struct_name" : struct_name , "fields" : fields , ** (context or {})}
219-
220- return engine .render_template ("go_struct" , template_context )
174+ return TemplateEngine (template_dir )
0 commit comments