Coverage for lobster/tools/trlc/conversion_rule.py: 88%

68 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-09-22 04:43 +0000

1# LOBSTER - Lightweight Open BMW Software Traceability Evidence Report 

2# Copyright (C) 2025-2026 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 

3# 

4# This program is free software: you can redistribute it and/or modify 

5# it under the terms of the GNU Affero General Public License as 

6# published by the Free Software Foundation, either version 3 of the 

7# License, or (at your option) any later version. 

8# 

9# This program is distributed in the hope that it will be useful, but 

10# WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

12# Affero General Public License for more details. 

13# 

14# You should have received a copy of the GNU Affero General Public 

15# License along with this program. If not, see 

16# <https://www.gnu.org/licenses/>. 

17 

18 

19from dataclasses import dataclass 

20from typing import Dict, Iterable, List, Optional, Union 

21 

22from lobster.tools.trlc.tag_entry import TagEntry 

23 

24 

25@dataclass 

26class ConversionRule: 

27 """Specifies how to convert TRLC Record_Objects to LOBSTER items.""" 

28 

29 def __init__( 

30 self, 

31 package: str, 

32 record_type: str, 

33 namespace: str, 

34 version_field: Optional[str] = None, 

35 description_fields: Optional[Union[str, Iterable[str]]] = None, 

36 justification_up_fields: Optional[Union[str, Iterable[str]]] = None, 

37 justification_down_fields: Optional[Union[str, Iterable[str]]] = None, 

38 justification_global_fields: Optional[Union[str, Iterable[str]]] = None, 

39 tags: Optional[Iterable[Union[str, Dict[str, str]]]] = None, 

40 applies_to_derived_types: bool = True, 

41 ): 

42 self._record_type_name = record_type 

43 self._package_name = package 

44 self._lobster_namespace = namespace 

45 self._version = version_field 

46 self._description_fields = self._as_string_list(description_fields) 

47 self._justification_up_fields = self._as_string_list(justification_up_fields) 

48 self._justification_down_fields = self._as_string_list( 

49 justification_down_fields) 

50 self._justification_global_fields = self._as_string_list( 

51 justification_global_fields) 

52 self._tags = self._as_tag_list(tags) 

53 self._applies_to_derived_types = applies_to_derived_types 

54 

55 def __hash__(self) -> int: 

56 return id(self) 

57 

58 @property 

59 def type_name(self) -> str: 

60 return self._record_type_name 

61 

62 @property 

63 def package_name(self) -> str: 

64 return self._package_name 

65 

66 @property 

67 def applies_to_derived_types(self) -> bool: 

68 return self._applies_to_derived_types 

69 

70 @property 

71 def lobster_namespace(self) -> str: 

72 return self._lobster_namespace 

73 

74 @property 

75 def version(self) -> str: 

76 return self._version 

77 

78 @staticmethod 

79 def _as_string_list(value: Optional[Union[str, Iterable[str]]]) -> List[str]: 

80 if value is None: 

81 return [] 

82 if isinstance(value, str): 82 ↛ 83line 82 didn't jump to line 83 because the condition on line 82 was never true

83 return [value] 

84 if isinstance(value, list): 84 ↛ 87line 84 didn't jump to line 87 because the condition on line 84 was always true

85 return value 

86 

87 raise ValueError(f"Expected str or list, got {type(value)}") 

88 

89 @staticmethod 

90 def _as_tag_list( 

91 tags: Optional[Iterable[Union[str, Dict[str, str]]]], 

92 ) -> List[TagEntry]: 

93 result = [] 

94 if tags is not None: 

95 for tag in tags: 

96 if isinstance(tag, str): 96 ↛ 98line 96 didn't jump to line 98 because the condition on line 96 was always true

97 result.append(TagEntry(field=tag)) 

98 elif isinstance(tag, dict): 

99 result.append(TagEntry(**tag)) 

100 else: 

101 raise ValueError(f"Expected str or dict, got {type(tag)}") 

102 return result 

103 

104 @property 

105 def tags(self) -> List[TagEntry]: 

106 return self._tags 

107 

108 @property 

109 def description_fields(self) -> List[str]: 

110 return self._description_fields 

111 

112 @property 

113 def justification_up_fields(self) -> List[str]: 

114 return self._justification_up_fields 

115 

116 @property 

117 def justification_down_fields(self) -> List[str]: 

118 return self._justification_down_fields 

119 

120 @property 

121 def justification_global_fields(self) -> List[str]: 

122 return self._justification_global_fields