
* Generate description and metadata for rules with no language, so that they get indexed. * Index rules with different types in language specializations. * Improve validation to reject new rules with no language specialization (i.e. only a predefined set of such rules is allowed because they were imported from Jira and kept for historical purposes). * Write smaller JSON files, reduce their size by 30%. * Improve test coverage of CLI application.
93 lines
2.7 KiB
Python
93 lines
2.7 KiB
Python
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Final, Generator, Iterable, Optional
|
|
from bs4 import BeautifulSoup
|
|
|
|
|
|
METADATA_FILE_NAME: Final[str] = 'metadata.json'
|
|
DESCRIPTION_FILE_NAME: Final[str] = 'rule.html'
|
|
|
|
class LanguageSpecificRule:
|
|
language_path: Final[Path]
|
|
rule: 'GenericRule'
|
|
__metadata: Optional[dict] = None
|
|
__description: Optional[object] = None
|
|
|
|
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)
|
|
try:
|
|
lang_metadata = json.loads(metadata_path.read_bytes())
|
|
except:
|
|
print('Failed to parse ', metadata_path)
|
|
raise
|
|
|
|
self.__metadata = self.rule.generic_metadata | lang_metadata
|
|
return self.__metadata
|
|
|
|
@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
|
|
|
|
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]
|
|
|
|
def __init__(self, rules_path: Path=DEFAULT_RULES_PATH):
|
|
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):
|
|
return GenericRule(self.rules_path.joinpath(ruleid))
|