• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aas-core-works / aas-core-codegen / 23051946462

13 Mar 2026 12:59PM UTC coverage: 83.657% (+1.2%) from 82.428%
23051946462

push

github

web-flow
Turn on coveralls (#596)

The site coveralls.io was down so we had to turn off the coverage
upload temporarily.

30831 of 36854 relevant lines covered (83.66%)

3.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/aas_core_codegen/csharp/common.py
1
"""Provide common functions shared among different C# code generation modules."""
2
import re
4✔
3
from typing import List, cast, Optional
4✔
4

5
from icontract import ensure, require
4✔
6

7
from aas_core_codegen import intermediate
4✔
8
from aas_core_codegen.common import Stripped, assert_never
4✔
9
from aas_core_codegen.csharp import naming as csharp_naming
4✔
10

11

12
@ensure(lambda result: result.startswith('"'))
4✔
13
@ensure(lambda result: result.endswith('"'))
4✔
14
def string_literal(text: str) -> Stripped:
4✔
15
    """Generate a C# string literal from the ``text``."""
16
    escaped = []  # type: List[str]
4✔
17

18
    for character in text:
4✔
19
        if character == "\a":
4✔
20
            escaped.append("\\a")
×
21
        elif character == "\b":
4✔
22
            escaped.append("\\b")
×
23
        elif character == "\f":
4✔
24
            escaped.append("\\f")
×
25
        elif character == "\n":
4✔
26
            escaped.append("\\n")
4✔
27
        elif character == "\r":
4✔
28
            escaped.append("\\r")
×
29
        elif character == "\t":
4✔
30
            escaped.append("\\t")
×
31
        elif character == "\v":
4✔
32
            escaped.append("\\v")
×
33
        elif character == '"':
4✔
34
            escaped.append('\\"')
4✔
35
        elif character == "\\":
4✔
36
            escaped.append("\\\\")
4✔
37
        else:
38
            escaped.append(character)
4✔
39

40
    return Stripped('"{}"'.format("".join(escaped)))
4✔
41

42

43
def needs_escaping(text: str) -> bool:
4✔
44
    """Check whether the ``text`` contains a character that needs escaping."""
45
    for character in text:
4✔
46
        if character == "\a":
4✔
47
            return True
×
48
        elif character == "\b":
4✔
49
            return True
×
50
        elif character == "\f":
4✔
51
            return True
×
52
        elif character == "\n":
4✔
53
            return True
×
54
        elif character == "\r":
4✔
55
            return True
×
56
        elif character == "\t":
4✔
57
            return True
×
58
        elif character == "\v":
4✔
59
            return True
×
60
        elif character == '"':
4✔
61
            return True
×
62
        elif character == "\\":
4✔
63
            return True
×
64
        else:
65
            pass
3✔
66

67
    return False
4✔
68

69

70
PRIMITIVE_TYPE_MAP = {
4✔
71
    intermediate.PrimitiveType.BOOL: Stripped("bool"),
72
    intermediate.PrimitiveType.INT: Stripped("long"),
73
    intermediate.PrimitiveType.FLOAT: Stripped("double"),
74
    intermediate.PrimitiveType.STR: Stripped("string"),
75
    intermediate.PrimitiveType.BYTEARRAY: Stripped("byte[]"),
76
}
77

78

79
def _assert_all_primitive_types_are_mapped() -> None:
4✔
80
    """Assert that we have explicitly mapped all the primitive types to C#."""
81
    all_primitive_literals = set(literal.value for literal in PRIMITIVE_TYPE_MAP)
4✔
82

83
    mapped_primitive_literals = set(
4✔
84
        literal.value for literal in intermediate.PrimitiveType
85
    )
86

87
    all_diff = all_primitive_literals.difference(mapped_primitive_literals)
4✔
88
    mapped_diff = mapped_primitive_literals.difference(all_primitive_literals)
4✔
89

90
    messages = []  # type: List[str]
4✔
91
    if len(mapped_diff) > 0:
4✔
92
        messages.append(
×
93
            f"More primitive maps are mapped than there were defined "
94
            f"in the ``intermediate._types``: {sorted(mapped_diff)}"
95
        )
96

97
    if len(all_diff) > 0:
4✔
98
        messages.append(
×
99
            f"One or more primitive types in the ``intermediate._types`` were not "
100
            f"mapped in PRIMITIVE_TYPE_MAP: {sorted(all_diff)}"
101
        )
102

103
    if len(messages) > 0:
4✔
104
        raise AssertionError("\n\n".join(messages))
×
105

106

107
_assert_all_primitive_types_are_mapped()
4✔
108

109

110
# fmt: off
111
@require(
4✔
112
    lambda our_type_qualifier:
113
    not (our_type_qualifier is not None)
114
    or not our_type_qualifier.endswith('.')
115
)
116
# fmt: on
117
def generate_type(
4✔
118
    type_annotation: intermediate.TypeAnnotationUnion,
119
    our_type_qualifier: Optional[Stripped] = None,
120
) -> Stripped:
121
    """
122
    Generate the C# type for the given type annotation.
123

124
    ``our_type_prefix`` is appended to all our types, if specified.
125
    """
126
    our_type_prefix = "" if our_type_qualifier is None else f"{our_type_qualifier}."
4✔
127
    if isinstance(type_annotation, intermediate.PrimitiveTypeAnnotation):
4✔
128
        return PRIMITIVE_TYPE_MAP[type_annotation.a_type]
4✔
129

130
    elif isinstance(type_annotation, intermediate.OurTypeAnnotation):
4✔
131
        our_type = type_annotation.our_type
4✔
132

133
        if isinstance(our_type, intermediate.Enumeration):
4✔
134
            return Stripped(
4✔
135
                our_type_prefix + csharp_naming.enum_name(type_annotation.our_type.name)
136
            )
137

138
        elif isinstance(our_type, intermediate.ConstrainedPrimitive):
4✔
139
            return PRIMITIVE_TYPE_MAP[our_type.constrainee]
4✔
140

141
        elif isinstance(our_type, intermediate.Class):
4✔
142
            # NOTE (mristin, 2023-02-08):
143
            # We want to allow custom enhancements and wrappings around
144
            # our model classes. Therefore, we always operate over C# interfaces
145
            # instead of concrete classes, even if the class is a concrete one and
146
            # has no concrete descendants.
147

148
            return Stripped(
4✔
149
                our_type_prefix + csharp_naming.interface_name(our_type.name)
150
            )
151

152
    elif isinstance(type_annotation, intermediate.ListTypeAnnotation):
4✔
153
        item_type = generate_type(
4✔
154
            type_annotation=type_annotation.items, our_type_qualifier=our_type_qualifier
155
        )
156

157
        return Stripped(f"List<{item_type}>")
4✔
158

159
    elif isinstance(type_annotation, intermediate.OptionalTypeAnnotation):
4✔
160
        value = generate_type(
4✔
161
            type_annotation=type_annotation.value, our_type_qualifier=our_type_qualifier
162
        )
163
        return Stripped(f"{value}?")
4✔
164

165
    else:
166
        assert_never(type_annotation)
×
167

168
    raise AssertionError("Should not have gotten here")
×
169

170

171
INDENT = "    "
4✔
172
INDENT2 = INDENT * 2
4✔
173
INDENT3 = INDENT * 3
4✔
174
INDENT4 = INDENT * 4
4✔
175
INDENT5 = INDENT * 5
4✔
176
INDENT6 = INDENT * 6
4✔
177
INDENT7 = INDENT * 7
4✔
178
INDENT8 = INDENT * 8
4✔
179

180
# noinspection RegExpSimplifiable
181
NAMESPACE_IDENTIFIER_RE = re.compile(
4✔
182
    r"[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*"
183
)
184

185

186
class NamespaceIdentifier(str):
4✔
187
    """Capture a namespace identifier."""
188

189
    @require(lambda identifier: NAMESPACE_IDENTIFIER_RE.fullmatch(identifier))
4✔
190
    def __new__(cls, identifier: str) -> "NamespaceIdentifier":
4✔
191
        return cast(NamespaceIdentifier, identifier)
4✔
192

193

194
WARNING = Stripped(
4✔
195
    """\
196
/*
197
 * This code has been automatically generated by aas-core-codegen.
198
 * Do NOT edit or append.
199
 */"""
200
)
201

202

203
# fmt: off
204
@ensure(
4✔
205
    lambda namespace, result:
206
    not (namespace != "Aas") or len(result) == 1,
207
    "Exactly one block of stripped text to be appended to the list of using directives "
208
    "if this using directive is necessary"
209
)
210
@ensure(
4✔
211
    lambda namespace, result:
212
    not (namespace == "Aas") or len(result) == 0,
213
    "Empty list if no directive is necessary"
214
)
215
# fmt: on
216
def generate_using_aas_directive_if_necessary(
4✔
217
    namespace: NamespaceIdentifier,
218
) -> List[Stripped]:
219
    """Generate the using directive if the namespace does not equal ``Aas``."""
220
    if namespace == "Aas":
4✔
221
        return []
×
222

223
    if namespace.endswith(".Aas"):
4✔
224
        return [Stripped(f"using Aas = {namespace};")]
×
225

226
    return [Stripped(f"using Aas = {namespace};  // renamed")]
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc