diff --git a/src/icmtoolchain/__init__.py b/src/icmtoolchain/__init__.py index a203b63..6470bd7 100644 --- a/src/icmtoolchain/__init__.py +++ b/src/icmtoolchain/__init__.py @@ -239,5 +239,5 @@ from .build_config import BuildConfig from .language import MakeDataConfig from .make_config import MakeConfig -MakeDataConfig.register_config("make.json", MakeConfig) -MakeDataConfig.register_config("build.config", BuildConfig) +MakeDataConfig.register("make.json", MakeConfig) +MakeDataConfig.register("build.config", BuildConfig) diff --git a/src/icmtoolchain/language.py b/src/icmtoolchain/language.py index 1c9fc1b..1cf20b3 100644 --- a/src/icmtoolchain/language.py +++ b/src/icmtoolchain/language.py @@ -356,7 +356,7 @@ class MakeDataConfig(FileConfig, metaclass=ABCMeta): ... @staticmethod - def register_config(criteria: Union[str, Callable[[str], str]], data: type['MakeDataConfig']) -> None: + def register(criteria: Union[str, Callable[[str], str]], data: type['MakeDataConfig']) -> None: """Here you can check if this project can be loaded with this config. Args: diff --git a/src/icmtoolchain/project_manager.py b/src/icmtoolchain/project_manager.py index c82d4c3..720d587 100644 --- a/src/icmtoolchain/project_manager.py +++ b/src/icmtoolchain/project_manager.py @@ -1,13 +1,91 @@ import json import os +from abc import ABCMeta, abstractmethod from os.path import abspath, basename, exists, isdir, isfile, join -from typing import Any, Dict, Final, List, Optional, Tuple +from typing import (Any, Callable, Dict, Final, List, Optional, Tuple, Union, + override) from . import GLOBALS from .config import Config +from .language import MakeDataConfig from .shell import abort, confirm_prompt, pretty_print, warn from .utils import ensure_not_whitespace, remove_tree +AVAILABLE_ARTIFACTS: Dict[Union[type, Callable[[Any], bool]], Union[Callable[[Any], 'Artifact'], type['Artifact']]] = {} + +class Artifact(metaclass=ABCMeta): + def __init__(self, description: Any) -> None: + self.description = description + + @abstractmethod + def fetch(self) -> None: + ... + + @abstractmethod + def as_project(self) -> MakeDataConfig: + ... + + @staticmethod + def register(criteria: Union[type, Callable[[Any], bool]], data: Union[Callable[[Any], 'Artifact'], type['Artifact']]) -> None: + """Here you can match right artifact to different types of dependencies. + + Args: + criteria (Union[str, Callable[[str], bool]]): filter artifact types by filename or deeper callable inspection + data (type[Artifact]): type to be created, which will become a artifact with data + + Raises: + ValueError: if this criteria has already been registered earlier + """ + if criteria in AVAILABLE_ARTIFACTS: + raise ValueError(f"Data criteria {criteria} already occupied by {AVAILABLE_ARTIFACTS[criteria]}!") + AVAILABLE_ARTIFACTS[criteria] = data + + @staticmethod + def of(description: Any) -> Optional['Artifact']: + for criteria, artifact_type in AVAILABLE_ARTIFACTS.items(): + if isinstance(criteria, type): + if not isinstance(description, criteria): + continue + elif callable(criteria): + if not criteria(description): + continue + else: + continue + return artifact_type(description) + +class ModBrowserArtifact(Artifact): + description: int + + def __init__(self, remote_id: int) -> None: + super().__init__(remote_id) + + @override + def fetch(self) -> None: + pass + + @override + def as_project(self) -> MakeDataConfig: + raise NotImplementedError() + +Artifact.register(int, ModBrowserArtifact) +Artifact.register(lambda description: isinstance(description, dict) and "projectId" in description, lambda description: ModBrowserArtifact(description["projectId"])) + +class RepositoryArtifact(Artifact): + description: str + + def __init__(self, remote_url: str) -> None: + super().__init__(remote_url) + + @override + def fetch(self) -> None: + pass + + @override + def as_project(self) -> MakeDataConfig: + raise NotImplementedError() + +Artifact.register(lambda description: isinstance(description, str) and "://" in description, RepositoryArtifact) +Artifact.register(lambda description: isinstance(description, dict) and "url" in description, lambda description: RepositoryArtifact(description["url"])) class ProjectManager: projects: Final[List[str]]