Coverage for src / ezcompiler / services / template_service.py: 84.04%
86 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 06:49 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 06:49 +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
24from ..adapters.base_file_writer import BaseFileWriter
25from ..adapters.disk_file_writer import DiskFileWriter
27# Local imports
28from ..assets.templates.template_loader import TemplateLoader
29from ..shared.exceptions import TemplateError, VersionError
30from ..utils.file_utils import FileUtils
31from ..utils.template_utils import TemplateProcessor
33# ///////////////////////////////////////////////////////////////
34# CLASSES
35# ///////////////////////////////////////////////////////////////
38class TemplateService:
39 """
40 Template processing and file generation service.
42 Orchestrates template loading, processing, and file generation for
43 configuration files, setup.py, and version information files.
45 Attributes:
46 _template_loader: TemplateLoader instance for template operations
47 _processor: TemplateProcessor instance for variable substitution
49 Example:
50 >>> service = TemplateService()
51 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
52 >>> service.generate_setup_file(config, Path("setup.py"))
53 >>> service.generate_version_file(config, Path("version_info.txt"))
54 """
56 # ////////////////////////////////////////////////
57 # INITIALIZATION
58 # ////////////////////////////////////////////////
60 def __init__(self, file_writer: BaseFileWriter | None = None) -> None:
61 """
62 Initialize the template service.
64 Creates TemplateLoader and TemplateProcessor instances for
65 template operations.
66 """
67 if TemplateLoader is None: 67 ↛ 68line 67 didn't jump to line 68 because the condition on line 67 was never true
68 raise TemplateError("TemplateLoader is not available")
69 if TemplateProcessor is None: 69 ↛ 70line 69 didn't jump to line 70 because the condition on line 69 was never true
70 raise TemplateError("TemplateProcessor is not available")
72 self._template_loader = TemplateLoader()
73 self._processor = TemplateProcessor()
74 self._file_writer = file_writer or DiskFileWriter()
76 # ////////////////////////////////////////////////
77 # CONFIG FILE GENERATION
78 # ////////////////////////////////////////////////
80 def generate_config_file(
81 self,
82 config: dict[str, Any],
83 output_path: Path,
84 format_type: str = "yaml",
85 ) -> None:
86 """
87 Generate a configuration file (YAML or JSON) from template.
89 Args:
90 config: Project configuration dictionary
91 output_path: Path where to save the config file
92 format_type: Format type ("yaml" or "json")
94 Raises:
95 TemplateError: If generation fails
97 Example:
98 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
99 >>> service.generate_config_file(config, Path("ezcompiler.yaml"), "yaml")
100 """
101 try:
102 # Ensure output directory exists
103 FileUtils.ensure_parent_directory_exists(output_path)
105 # Process template
106 content = self._template_loader.process_config_template(format_type, config)
108 # Write file
109 self._file_writer.write_text(output_path, content, encoding="utf-8")
111 except TemplateError:
112 raise
113 except Exception as e:
114 raise TemplateError(f"Failed to generate config file: {str(e)}") from e
116 # ////////////////////////////////////////////////
117 # SETUP FILE GENERATION
118 # ////////////////////////////////////////////////
120 def generate_setup_file(
121 self,
122 config: dict[str, Any],
123 output_path: Path | None = None,
124 output_dir: Path | None = None,
125 ) -> Path:
126 """
127 Generate a setup.py file from template.
129 Args:
130 config: Project configuration dictionary
131 output_path: Direct path to setup.py file (optional)
132 output_dir: Directory where to save setup.py (optional, defaults to current dir)
134 Returns:
135 Path: Path to the generated setup.py file
137 Raises:
138 TemplateError: If generation fails
140 Example:
141 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
142 >>> setup_path = service.generate_setup_file(config, output_dir=Path("build"))
143 """
144 try:
145 # Determine output path
146 final_path: Path
147 if output_path is not None:
148 final_path = Path(output_path)
149 FileUtils.ensure_parent_directory_exists(final_path)
150 else:
151 target_dir = output_dir if output_dir is not None else Path.cwd()
152 target_dir = Path(target_dir)
153 target_dir.mkdir(parents=True, exist_ok=True)
154 final_path = target_dir / "setup.py"
156 # Process template
157 content = self._template_loader.process_setup_template("py", config)
159 # Write file
160 self._file_writer.write_text(final_path, content, encoding="utf-8")
162 return final_path
164 except TemplateError:
165 raise
166 except Exception as e:
167 raise TemplateError(f"Failed to generate setup file: {str(e)}") from e
169 # ////////////////////////////////////////////////
170 # VERSION FILE GENERATION
171 # ////////////////////////////////////////////////
173 def generate_version_file(
174 self,
175 config: dict[str, Any],
176 output_path: Path | None = None,
177 format_type: str = "txt",
178 ) -> Path:
179 """
180 Generate a version information file from template.
182 Args:
183 config: Project configuration dictionary
184 output_path: Path where to save the version file (optional)
185 format_type: Template format type (default: "txt")
187 Returns:
188 Path: Path to the generated version file
190 Raises:
191 VersionError: If generation fails
193 Note:
194 If output_path is None, uses version_filename and output_folder from config.
196 Example:
197 >>> config = {
198 ... "version": "1.0.0",
199 ... "project_name": "MyApp",
200 ... "version_filename": "version_info.txt",
201 ... "output_folder": "dist"
202 ... }
203 >>> version_path = service.generate_version_file(config)
204 """
205 try:
206 # Determine output path
207 final_path: Path
208 if output_path is not None:
209 final_path = Path(output_path)
210 else:
211 version_file = config.get("version_filename", "version_info.txt")
212 output_folder = Path(config.get("output_folder", "dist"))
213 final_path = output_folder / version_file
215 # Ensure output directory exists
216 FileUtils.ensure_parent_directory_exists(final_path)
218 # Extract required fields with defaults
219 version = config.get("version", "1.0.0")
220 company_name = config.get("company_name", "")
221 project_description = config.get("project_description", "")
222 project_name = config.get("project_name", "MyProject")
224 # Process template
225 content = self._template_loader.process_version_template(
226 format_type=format_type,
227 version=version,
228 company_name=company_name,
229 project_description=project_description,
230 project_name=project_name,
231 )
233 # Write file
234 self._file_writer.write_text(final_path, content, encoding="utf-8")
236 return final_path
238 except VersionError:
239 raise
240 except Exception as e:
241 raise VersionError(f"Failed to generate version file: {str(e)}") from e
243 # ////////////////////////////////////////////////
244 # TEMPLATE UTILITIES
245 # ////////////////////////////////////////////////
247 def process_config_template(self, format_type: str, config: dict[str, Any]) -> str:
248 """
249 Process a configuration template with values.
251 Args:
252 format_type: Format type ("yaml" or "json")
253 config: Configuration dictionary
255 Returns:
256 str: Processed template content
258 Example:
259 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
260 >>> content = service.process_config_template("yaml", config)
261 """
262 return self._template_loader.process_config_template(format_type, config)
264 def process_setup_template(self, config: dict[str, Any]) -> str:
265 """
266 Process a setup template with values.
268 Args:
269 config: Configuration dictionary
271 Returns:
272 str: Processed template content
274 Example:
275 >>> config = {"version": "1.0.0", "project_name": "MyApp"}
276 >>> content = service.process_setup_template(config)
277 """
278 return self._template_loader.process_setup_template("py", config)
280 def process_version_template(
281 self,
282 version: str,
283 company_name: str,
284 project_description: str,
285 project_name: str,
286 format_type: str = "txt",
287 ) -> str:
288 """
289 Process a version template with values.
291 Args:
292 version: Project version
293 company_name: Company name
294 project_description: Project description
295 project_name: Project name
296 format_type: Format type (default: "txt")
298 Returns:
299 str: Processed template content
301 Example:
302 >>> content = service.process_version_template(
303 ... "1.0.0", "MyCompany", "Description", "MyApp"
304 ... )
305 """
306 return self._template_loader.process_version_template(
307 format_type, version, company_name, project_description, project_name
308 )
310 # ////////////////////////////////////////////////
311 # TEMPLATE MANAGEMENT
312 # ////////////////////////////////////////////////
314 def list_available_templates(self) -> dict[str, list[str]]:
315 """
316 List all available templates.
318 Returns:
319 dict[str, list[str]]: Dictionary mapping template types to available formats
321 Example:
322 >>> templates = service.list_available_templates()
323 >>> print(templates)
324 {'config': ['yaml', 'json'], 'version': ['txt'], 'setup': ['py']}
325 """
326 return self._template_loader.list_available_templates()
328 def validate_template(self, template_type: str, format_type: str) -> bool:
329 """
330 Validate a template file.
332 Args:
333 template_type: Type of template (config, version, setup)
334 format_type: Format of the template
336 Returns:
337 bool: True if template is valid, False otherwise
339 Example:
340 >>> is_valid = service.validate_template("config", "yaml")
341 """
342 return self._template_loader.validate_template(template_type, format_type)
344 # ////////////////////////////////////////////////
345 # RAW TEMPLATE GENERATION
346 # ////////////////////////////////////////////////
348 def generate_mockup_template(
349 self,
350 template_type: str,
351 format_type: str,
352 output_path: Path,
353 ) -> None:
354 """
355 Generate a template file with mockup values instead of placeholders.
357 Args:
358 template_type: Type of template (config, version, setup)
359 format_type: Format of the template (yaml, json, py, txt)
360 output_path: Path where to save the generated file
362 Raises:
363 TemplateError: If generation fails
365 Example:
366 >>> service.generate_mockup_template("config", "yaml", Path("ezcompiler.yaml"))
367 """
368 try:
369 self._template_loader.generate_template_with_mockup(
370 template_type, format_type, output_path
371 )
372 except TemplateError:
373 raise
374 except Exception as e:
375 raise TemplateError(f"Failed to generate mockup template: {str(e)}") from e
377 def generate_raw_template(
378 self,
379 template_type: str,
380 format_type: str,
381 output_path: Path,
382 ) -> None:
383 """
384 Generate a raw template file with placeholders.
386 Args:
387 template_type: Type of template (config, version, setup)
388 format_type: Format of the template (yaml, json, py, txt)
389 output_path: Path where to save the template file
391 Raises:
392 TemplateError: If generation fails
394 Example:
395 >>> service.generate_raw_template("config", "yaml", Path("template.yaml"))
396 """
397 try:
398 self._template_loader.generate_raw_template(
399 template_type, format_type, output_path
400 )
401 except TemplateError:
402 raise
403 except Exception as e:
404 raise TemplateError(f"Failed to generate raw template: {str(e)}") from e