Coverage for src / ezcompiler / services / template_service.py: 83.87%
85 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 00:22 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 00:22 +0000
1# ///////////////////////////////////////////////////////////////
2# TEMPLATE_SERVICE - Template processing and file generation service
3# Project: ezcompiler
4# ///////////////////////////////////////////////////////////////
6"""
7Template service - Template processing and file generation service for EzCompiler.
9This module provides the TemplateService class that orchestrates template
10processing and file generation (setup.py, version_info.txt, config files).
12Services layer can use WARNING and ERROR log levels.
13"""
15from __future__ import annotations
17# ///////////////////////////////////////////////////////////////
18# IMPORTS
19# ///////////////////////////////////////////////////////////////
20# Standard library imports
21from pathlib import Path
22from typing import Any
24# Local imports
25from ..adapters import BaseFileWriter
26from ..adapters._disk_file_writer import DiskFileWriter
27from ..assets import TemplateLoader
28from ..shared.exceptions import TemplateError, VersionError
29from ..utils import FileUtils, TemplateProcessor
31# ///////////////////////////////////////////////////////////////
32# CLASSES
33# ///////////////////////////////////////////////////////////////
36class TemplateService:
37 """
38 Template processing and file generation service.
40 Orchestrates template loading, processing, and file generation for
41 configuration files, setup.py, and version information files.
43 Attributes:
44 _template_loader: TemplateLoader instance for template operations
45 _processor: TemplateProcessor instance for variable substitution
47 Example:
48 >>> service = TemplateService()
49 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
50 >>> service.generate_setup_file(config, Path("setup.py"))
51 >>> service.generate_version_file(config, Path("version_info.txt"))
52 """
54 # ////////////////////////////////////////////////
55 # INITIALIZATION
56 # ////////////////////////////////////////////////
58 def __init__(self, file_writer: BaseFileWriter | None = None) -> None:
59 """
60 Initialize the template service.
62 Creates TemplateLoader and TemplateProcessor instances for
63 template operations.
64 """
65 if TemplateLoader is None: 65 ↛ 66line 65 didn't jump to line 66 because the condition on line 65 was never true
66 raise TemplateError("TemplateLoader is not available")
67 if TemplateProcessor is None: 67 ↛ 68line 67 didn't jump to line 68 because the condition on line 67 was never true
68 raise TemplateError("TemplateProcessor is not available")
70 self._template_loader = TemplateLoader()
71 self._processor = TemplateProcessor()
72 self._file_writer = file_writer or DiskFileWriter()
74 # ////////////////////////////////////////////////
75 # CONFIG FILE GENERATION
76 # ////////////////////////////////////////////////
78 def generate_config_file(
79 self,
80 config: dict[str, Any],
81 output_path: Path,
82 format_type: str = "yaml",
83 ) -> None:
84 """
85 Generate a configuration file (YAML or JSON) from template.
87 Args:
88 config: Project configuration dictionary
89 output_path: Path where to save the config file
90 format_type: Format type ("yaml" or "json")
92 Raises:
93 TemplateError: If generation fails
95 Example:
96 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
97 >>> service.generate_config_file(config, Path("ezcompiler.yaml"), "yaml")
98 """
99 try:
100 # Ensure output directory exists
101 FileUtils.ensure_parent_directory_exists(output_path)
103 # Process template
104 content = self._template_loader.process_config_template(format_type, config)
106 # Write file
107 self._file_writer.write_text(output_path, content, encoding="utf-8")
109 except TemplateError:
110 raise
111 except Exception as e:
112 raise TemplateError(f"Failed to generate config file: {str(e)}") from e
114 # ////////////////////////////////////////////////
115 # SETUP FILE GENERATION
116 # ////////////////////////////////////////////////
118 def generate_setup_file(
119 self,
120 config: dict[str, Any],
121 output_path: Path | None = None,
122 output_dir: Path | None = None,
123 ) -> Path:
124 """
125 Generate a setup.py file from template.
127 Args:
128 config: Project configuration dictionary
129 output_path: Direct path to setup.py file (optional)
130 output_dir: Directory where to save setup.py (optional, defaults to current dir)
132 Returns:
133 Path: Path to the generated setup.py file
135 Raises:
136 TemplateError: If generation fails
138 Example:
139 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
140 >>> setup_path = service.generate_setup_file(config, output_dir=Path("build"))
141 """
142 try:
143 # Determine output path
144 final_path: Path
145 if output_path is not None:
146 final_path = Path(output_path)
147 FileUtils.ensure_parent_directory_exists(final_path)
148 else:
149 target_dir = output_dir if output_dir is not None else Path.cwd()
150 target_dir = Path(target_dir)
151 target_dir.mkdir(parents=True, exist_ok=True)
152 final_path = target_dir / "setup.py"
154 # Process template
155 content = self._template_loader.process_setup_template("py", config)
157 # Write file
158 self._file_writer.write_text(final_path, content, encoding="utf-8")
160 return final_path
162 except TemplateError:
163 raise
164 except Exception as e:
165 raise TemplateError(f"Failed to generate setup file: {str(e)}") from e
167 # ////////////////////////////////////////////////
168 # VERSION FILE GENERATION
169 # ////////////////////////////////////////////////
171 def generate_version_file(
172 self,
173 config: dict[str, Any],
174 output_path: Path | None = None,
175 format_type: str = "txt",
176 ) -> Path:
177 """
178 Generate a version information file from template.
180 Args:
181 config: Project configuration dictionary
182 output_path: Path where to save the version file (optional)
183 format_type: Template format type (default: "txt")
185 Returns:
186 Path: Path to the generated version file
188 Raises:
189 VersionError: If generation fails
191 Note:
192 If output_path is None, uses version_filename and output_folder from config.
194 Example:
195 >>> config = {
196 ... "version": "1.0.0",
197 ... "project_name": "MyApp",
198 ... "version_filename": "version_info.txt",
199 ... "output_folder": "dist"
200 ... }
201 >>> version_path = service.generate_version_file(config)
202 """
203 try:
204 # Determine output path
205 final_path: Path
206 if output_path is not None:
207 final_path = Path(output_path)
208 else:
209 version_file = config.get("version_filename", "version_info.txt")
210 output_folder = Path(config.get("output_folder", "dist"))
211 final_path = output_folder / version_file
213 # Ensure output directory exists
214 FileUtils.ensure_parent_directory_exists(final_path)
216 # Extract required fields with defaults
217 version = config.get("version", "1.0.0")
218 company_name = config.get("company_name", "")
219 project_description = config.get("project_description", "")
220 project_name = config.get("project_name", "MyProject")
222 # Process template
223 content = self._template_loader.process_version_template(
224 format_type=format_type,
225 version=version,
226 company_name=company_name,
227 project_description=project_description,
228 project_name=project_name,
229 )
231 # Write file
232 self._file_writer.write_text(final_path, content, encoding="utf-8")
234 return final_path
236 except VersionError:
237 raise
238 except Exception as e:
239 raise VersionError(f"Failed to generate version file: {str(e)}") from e
241 # ////////////////////////////////////////////////
242 # TEMPLATE UTILITIES
243 # ////////////////////////////////////////////////
245 def process_config_template(self, format_type: str, config: dict[str, Any]) -> str:
246 """
247 Process a configuration template with values.
249 Args:
250 format_type: Format type ("yaml" or "json")
251 config: Configuration dictionary
253 Returns:
254 str: Processed template content
256 Example:
257 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
258 >>> content = service.process_config_template("yaml", config)
259 """
260 return self._template_loader.process_config_template(format_type, config)
262 def process_setup_template(self, config: dict[str, Any]) -> str:
263 """
264 Process a setup template with values.
266 Args:
267 config: Configuration dictionary
269 Returns:
270 str: Processed template content
272 Example:
273 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
274 >>> content = service.process_setup_template(config)
275 """
276 return self._template_loader.process_setup_template("py", config)
278 def process_version_template(
279 self,
280 version: str,
281 company_name: str,
282 project_description: str,
283 project_name: str,
284 format_type: str = "txt",
285 ) -> str:
286 """
287 Process a version template with values.
289 Args:
290 version: Project version
291 company_name: Company name
292 project_description: Project description
293 project_name: Project name
294 format_type: Format type (default: "txt")
296 Returns:
297 str: Processed template content
299 Example:
300 >>> content = service.process_version_template(
301 ... "1.0.0", "MyCompany", "Description", "MyApp"
302 ... )
303 """
304 return self._template_loader.process_version_template(
305 format_type, version, company_name, project_description, project_name
306 )
308 # ////////////////////////////////////////////////
309 # TEMPLATE MANAGEMENT
310 # ////////////////////////////////////////////////
312 def list_available_templates(self) -> dict[str, list[str]]:
313 """
314 List all available templates.
316 Returns:
317 dict[str, list[str]]: Dictionary mapping template types to available formats
319 Example:
320 >>> templates = service.list_available_templates()
321 >>> print(templates)
322 {'config': ['yaml', 'json'], 'version': ['txt'], 'setup': ['py']}
323 """
324 return self._template_loader.list_available_templates()
326 def validate_template(self, template_type: str, format_type: str) -> bool:
327 """
328 Validate a template file.
330 Args:
331 template_type: Type of template (config, version, setup)
332 format_type: Format of the template
334 Returns:
335 bool: True if template is valid, False otherwise
337 Example:
338 >>> is_valid = service.validate_template("config", "yaml")
339 """
340 return self._template_loader.validate_template(template_type, format_type)
342 # ////////////////////////////////////////////////
343 # RAW TEMPLATE GENERATION
344 # ////////////////////////////////////////////////
346 def generate_mockup_template(
347 self,
348 template_type: str,
349 format_type: str,
350 output_path: Path,
351 ) -> None:
352 """
353 Generate a template file with mockup values instead of placeholders.
355 Args:
356 template_type: Type of template (config, version, setup)
357 format_type: Format of the template (yaml, json, py, txt)
358 output_path: Path where to save the generated file
360 Raises:
361 TemplateError: If generation fails
363 Example:
364 >>> service.generate_mockup_template("config", "yaml", Path("ezcompiler.yaml"))
365 """
366 try:
367 self._template_loader.generate_template_with_mockup(
368 template_type, format_type, output_path
369 )
370 except TemplateError:
371 raise
372 except Exception as e:
373 raise TemplateError(f"Failed to generate mockup template: {str(e)}") from e
375 def generate_raw_template(
376 self,
377 template_type: str,
378 format_type: str,
379 output_path: Path,
380 ) -> None:
381 """
382 Generate a raw template file with placeholders.
384 Args:
385 template_type: Type of template (config, version, setup)
386 format_type: Format of the template (yaml, json, py, txt)
387 output_path: Path where to save the template file
389 Raises:
390 TemplateError: If generation fails
392 Example:
393 >>> service.generate_raw_template("config", "yaml", Path("template.yaml"))
394 """
395 try:
396 self._template_loader.generate_raw_template(
397 template_type, format_type, output_path
398 )
399 except TemplateError:
400 raise
401 except Exception as e:
402 raise TemplateError(f"Failed to generate raw template: {str(e)}") from e