2021-02-23 20:41:11 +01:00
|
|
|
|
|
|
|
import json
|
|
|
|
from pathlib import Path
|
|
|
|
from typing import Final, Generator, Iterable, Optional
|
2021-05-04 09:58:49 +02:00
|
|
|
from bs4 import BeautifulSoup
|
2021-02-23 20:41:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
METADATA_FILE_NAME: Final[str] = 'metadata.json'
|
2021-05-04 09:58:49 +02:00
|
|
|
DESCRIPTION_FILE_NAME: Final[str] = 'rule.html'
|
2021-02-23 20:41:11 +01:00
|
|
|
|
|
|
|
class LanguageSpecificRule:
|
|
|
|
language_path: Final[Path]
|
|
|
|
rule: 'GenericRule'
|
|
|
|
__metadata: Optional[dict] = None
|
2021-05-04 09:58:49 +02:00
|
|
|
__description: Optional[object] = None
|
2021-02-23 20:41:11 +01:00
|
|
|
|
|
|
|
def __init__(self, language_path: Path, rule: 'GenericRule'):
|
|
|
|
self.language_path = language_path
|
|
|
|
self.rule = rule
|
|
|
|
|
|
|
|
@property
|
|
|
|
def language(self):
|
|
|
|
return self.language_path.name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def id(self):
|
|
|
|
return f'{self.language}:{self.rule.id}'
|
|
|
|
|
|
|
|
@property
|
|
|
|
def metadata(self):
|
|
|
|
if self.__metadata is not None:
|
|
|
|
return self.__metadata
|
|
|
|
metadata_path = self.language_path.joinpath(METADATA_FILE_NAME)
|
2021-09-22 15:33:20 +02:00
|
|
|
try:
|
|
|
|
lang_metadata = json.loads(metadata_path.read_bytes())
|
|
|
|
except:
|
|
|
|
print('Failed to parse ', metadata_path)
|
|
|
|
raise
|
|
|
|
|
2021-02-23 20:41:11 +01:00
|
|
|
self.__metadata = self.rule.generic_metadata | lang_metadata
|
|
|
|
return self.__metadata
|
|
|
|
|
2021-05-04 09:58:49 +02:00
|
|
|
@property
|
|
|
|
def description(self):
|
|
|
|
if self.__description is not None:
|
|
|
|
return self.__description
|
|
|
|
description_path = self.language_path.joinpath(DESCRIPTION_FILE_NAME)
|
|
|
|
soup = BeautifulSoup(description_path.read_bytes(),features="html.parser")
|
|
|
|
self.__description = soup
|
|
|
|
return self.__description
|
2021-02-23 20:41:11 +01:00
|
|
|
|
|
|
|
class GenericRule:
|
|
|
|
rule_path: Final[Path]
|
|
|
|
__generic_metadata: Optional[dict] = None
|
|
|
|
|
|
|
|
def __init__(self, rule_path: Path):
|
|
|
|
self.rule_path = rule_path
|
|
|
|
|
|
|
|
@property
|
|
|
|
def id(self) -> str:
|
|
|
|
return self.rule_path.name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def specializations(self) -> Generator[LanguageSpecificRule, None, None]:
|
|
|
|
return (LanguageSpecificRule(child, self) for child in self.rule_path.iterdir() if child.is_dir())
|
|
|
|
|
|
|
|
def get_language(self, language: str) -> LanguageSpecificRule:
|
|
|
|
return LanguageSpecificRule(self.rule_path.joinpath(language), self)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def generic_metadata(self):
|
|
|
|
if self.__generic_metadata is not None:
|
|
|
|
return self.__generic_metadata
|
|
|
|
metadata_path = self.rule_path.joinpath(METADATA_FILE_NAME)
|
|
|
|
self.__generic_metadata = json.loads(metadata_path.read_bytes())
|
|
|
|
return self.__generic_metadata
|
|
|
|
|
|
|
|
|
|
|
|
class RulesRepository:
|
|
|
|
DEFAULT_RULES_PATH: Final[Path] = Path(__file__).parent.parent.parent.joinpath('rules')
|
|
|
|
|
|
|
|
rules_path: Final[Path]
|
|
|
|
|
2022-01-28 09:51:13 +01:00
|
|
|
def __init__(self, rules_path: Path=DEFAULT_RULES_PATH):
|
2021-02-23 20:41:11 +01:00
|
|
|
self.rules_path = rules_path
|
|
|
|
|
|
|
|
@property
|
|
|
|
def rules(self) -> Generator[GenericRule, None, None]:
|
|
|
|
return (GenericRule(child) for child in self.rules_path.glob('S*') if child.is_dir())
|
|
|
|
|
|
|
|
def get_rule(self, ruleid: str):
|
2021-05-04 09:58:49 +02:00
|
|
|
return GenericRule(self.rules_path.joinpath(ruleid))
|