import importlib import os import sys import textwrap import traceback from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC from cpl_cli.command_abc import CommandABC from cpl_cli.configuration import WorkspaceSettings from cpl_cli.configuration.schematic_collection import SchematicCollection from cpl_cli.helper.dependencies import Dependencies from cpl_core.configuration.configuration_abc import ConfigurationABC from cpl_core.console.console import Console from cpl_core.console.foreground_color_enum import ForegroundColorEnum from cpl_core.utils.string import String class GenerateService(CommandABC): def __init__( self, configuration: ConfigurationABC, workspace: WorkspaceSettings, ): """ Service for the CLI command generate :param configuration: """ CommandABC.__init__(self) self._config = configuration self._workspace = workspace self._config = configuration self._env = self._config.environment self._schematics = {} self._schematic_classes = set() for package_name, version in Dependencies.get_cpl_packages(): if package_name == "cpl-cli": continue package = importlib.import_module(String.convert_to_snake_case(package_name)) self._read_custom_schematics_from_path(os.path.dirname(package.__file__)) self._read_custom_schematics_from_path(self._env.working_directory) self._read_custom_schematics_from_path(self._env.runtime_directory) if len(self._schematic_classes) == 0: Console.error(f"No schematics found in template directory: .cpl") sys.exit() known_schematics = [] for schematic in self._schematic_classes: known_schematics.append(schematic.__name__) schematic.register() self._schematics = SchematicCollection.get_schematics() @property def help_message(self) -> str: schematics = [] for schematic in self._schematics: aliases = "|".join(self._schematics[schematic]["Aliases"]) schematic_str = schematic if len(aliases) > 0: schematic_str = f"{schematic} ({aliases})" schematics.append(schematic_str) help_msg = textwrap.dedent( """\ Generate a file based on schematic. Usage: cpl generate Arguments: schematic: The schematic to generate. name: The name of the generated file Schematics:""" ) for schematic in schematics: help_msg += f"\n {schematic}" return help_msg def _read_custom_schematics_from_path(self, path: str): if not os.path.exists(os.path.join(path, ".cpl")): return sys.path.insert(0, os.path.join(path, ".cpl")) for r, d, f in os.walk(os.path.join(path, ".cpl")): for file in f: if not file.startswith("schematic_") or not file.endswith(".py"): continue try: exec(open(os.path.join(r, file), "r").read()) self._schematic_classes.update(GenerateSchematicABC.__subclasses__()) except Exception as e: Console.error(str(e), traceback.format_exc()) sys.exit(-1) @staticmethod def _create_file(file_path: str, value: str): """ Creates the given file with content :param file_path: :param value: :return: """ with open(file_path, "w") as template: template.write(value) template.close() def _create_init_files( self, file_path: str, template: GenerateSchematicABC, class_name: str, schematic: str, rel_path: str ): if not os.path.isdir(os.path.dirname(file_path)): os.makedirs(os.path.dirname(file_path)) directory = "" for subdir in template.path.split("/"): directory = os.path.join(directory, subdir) if subdir == "src": continue file = self._schematics["init"]["Template"](class_name, "init", rel_path) if os.path.exists(os.path.join(os.path.abspath(directory), file.name)): continue Console.spinner( f"Creating {os.path.abspath(directory)}/{file.name}", self._create_file, os.path.join(os.path.abspath(directory), file.name), file.get_code(), text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.cyan, ) def _generate(self, schematic: str, name: str, template: type): """ Generates files by given schematic, name and template :param schematic: :param name: :param template: :return: """ class_name = name rel_path = "" if "/" in name: parts = name.split("/") rel_path = "/".join(parts[:-1]) class_name = parts[len(parts) - 1] if self._workspace is not None and parts[0] in self._workspace.projects: rel_path = os.path.join(os.path.dirname(self._workspace.projects[parts[0]]), *parts[1:-1]) template = template(class_name, String.convert_to_snake_case(schematic), rel_path) file_path = os.path.join(self._env.working_directory, template.path, template.name) self._create_init_files(file_path, template, class_name, schematic, rel_path) if os.path.isfile(file_path): Console.error(f"{String.first_to_upper(schematic)} already exists!\n") sys.exit() message = f"Creating {self._env.working_directory}/{template.path}/{template.name}" if template.path == "": message = f"Creating {self._env.working_directory}/{template.name}" Console.spinner( message, self._create_file, file_path, template.get_code(), text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.cyan, ) def _get_schematic_by_alias(self, schematic: str) -> str: for key in self._schematics: if schematic in self._schematics[key]["Aliases"]: return key return schematic def execute(self, args: list[str]): """ Entry point of command :param args: :return: """ schematic = None value = None for s in self._schematics: value = self._config.get_configuration(s) if value is not None: schematic = s break if ( schematic is None and len(args) >= 1 and (args[0] in self._schematics or self._get_schematic_by_alias(args[0]) != args[0]) ): schematic = self._get_schematic_by_alias(args[0]) self._config.add_configuration(schematic, args[1]) value = args[1] if schematic is None: Console.error(f"Schematic not found") Console.write_line(self.help_message) sys.exit() name = value if name is None: name = Console.read(f"Name for the {args[0]}: ") if schematic in self._schematics: s = self._schematics[schematic] self._generate(schematic, name, s["Template"]) else: self._help("Usage: cpl generate [options]") Console.write_line() sys.exit()