Coverage for glotter/project.py: 100%
75 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-08 17:57 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-08 17:57 +0000
1from enum import Enum, auto
2from typing import Annotated, ClassVar, Dict, List, Optional
4from pydantic import BaseModel, Field, ValidationInfo, field_validator
6from glotter.auto_gen_test import AutoGenTest, AutoGenUseTests
7from glotter.errors import raise_simple_validation_error, validate_str_list
10class NamingScheme(Enum):
11 hyphen = auto()
12 underscore = auto()
13 camel = auto()
14 pascal = auto()
15 lower = auto()
18class AcronymScheme(Enum):
19 lower = "lower"
20 upper = "upper"
21 two_letter_limit = "two_letter_limit"
24class Project(BaseModel):
25 VALID_REGEX: ClassVar[str] = "^[0-9a-zA-Z]+$"
27 words: Annotated[
28 List[Annotated[str, Field(min_length=1, pattern=VALID_REGEX, strict=True)]],
29 Field(min_length=1, strict=True),
30 ]
31 requires_parameters: bool = False
32 acronyms: List[Annotated[str, Field(min_length=1, pattern=VALID_REGEX, strict=True)]] = []
33 acronym_scheme: AcronymScheme = AcronymScheme.two_letter_limit
34 use_tests: Optional[AutoGenUseTests] = None
35 tests: Dict[str, AutoGenTest] = {}
37 @field_validator("acronyms", mode="before")
38 @classmethod
39 def get_acronym(cls, values):
40 validate_str_list(cls, values)
41 return [value.upper() for value in values]
43 @field_validator("tests", mode="before")
44 @classmethod
45 def get_tests(cls, value, info: ValidationInfo):
46 if not isinstance(value, dict) or not all(
47 isinstance(test, dict) for test in value.values()
48 ):
49 return value
51 if info.data.get("use_tests"):
52 raise_simple_validation_error(
53 cls, '"tests" and "use_tests" items are mutually exclusive', {"use_tests": "..."}
54 )
56 return {
57 test_name: {
58 **test,
59 "requires_parameters": info.data.get("requires_parameters") or False,
60 "name": test_name,
61 }
62 for test_name, test in value.items()
63 }
65 def set_tests(self, project: "Project"):
66 """
67 If there is a "use_tests" item, then set the specified tests, renaming them
68 according to the "use_tests" item. The "use_tests" item is then removed
70 :params tests: Project with tests to use
71 """
73 if self.use_tests:
74 self.tests = {}
75 for test_name_, test in project.tests.items():
76 test_name = test_name_.replace(self.use_tests.search, self.use_tests.replace)
77 self.tests[test_name] = AutoGenTest(
78 **test.model_dump(exclude={"name"}), name=test_name
79 )
81 self.requires_parameters = project.requires_parameters
82 self.use_tests = None
84 @property
85 def display_name(self):
86 return self._as_display()
88 def get_project_name_by_scheme(self, naming):
89 """
90 gets a project name for a specific naming scheme
92 :param naming: the naming scheme
93 :return: the project type formatted by the directory's naming scheme
94 """
95 try:
96 return {
97 NamingScheme.hyphen: self._as_hyphen(),
98 NamingScheme.underscore: self._as_underscore(),
99 NamingScheme.camel: self._as_camel(),
100 NamingScheme.pascal: self._as_pascal(),
101 NamingScheme.lower: self._as_lower(),
102 }[naming]
103 except KeyError as e:
104 raise KeyError(f'Unknown naming scheme "{naming}"') from e
106 def _as_hyphen(self):
107 return "-".join(self._try_as_acronym(word, NamingScheme.hyphen) for word in self.words)
109 def _as_underscore(self):
110 return "_".join(self._try_as_acronym(word, NamingScheme.underscore) for word in self.words)
112 def _as_camel(self):
113 return self.words[0].lower() + "".join(
114 self._try_as_acronym(word.title(), NamingScheme.camel) for word in self.words[1:]
115 )
117 def _as_pascal(self):
118 return "".join(
119 self._try_as_acronym(word.title(), NamingScheme.pascal) for word in self.words
120 )
122 def _as_lower(self):
123 return "".join(word.lower() for word in self.words)
125 def _as_display(self):
126 return " ".join(
127 self._try_as_acronym(word.title(), NamingScheme.underscore) for word in self.words
128 )
130 def _is_acronym(self, word):
131 return word.upper() in self.acronyms
133 def _try_as_acronym(self, word, naming_scheme):
134 if self._is_acronym(word):
135 if self.acronym_scheme == AcronymScheme.upper:
136 return word.upper()
137 elif self.acronym_scheme == AcronymScheme.lower:
138 return word.lower()
139 elif len(word) <= 2 and naming_scheme in [
140 NamingScheme.camel,
141 NamingScheme.pascal,
142 ]:
143 return word.upper()
145 return word