1from __future__ import annotations
2
3from enum import IntFlag
4
5from dissect.cstruct.types.base import BaseType
6from dissect.cstruct.types.enum import PY_311, EnumMetaType
7
8
9class Flag(BaseType, IntFlag, metaclass=EnumMetaType):
10 """Flag type supercharged with cstruct functionality.
11
12 Flags are (mostly) compatible with the Python 3 standard library ``IntFlag`` with some notable differences:
13 - Flag members are only considered equal if the flag class is the same
14
15 Flags can be made using any integer type.
16
17 Example:
18 When using the default C-style parser, the following syntax is supported::
19
20 flag <name> [: <type>] {
21 <values>
22 };
23
24 For example, a flag that has A=1, B=4 and C=8 could be written like so::
25
26 flag Test : uint16 {
27 A, B=4, C
28 };
29 """
30
31 def __repr__(self) -> str:
32 result = super().__repr__()
33 if not self.__class__.__name__:
34 # Deal with anonymous flags by stripping off the first bit
35 # I.e. <.RED: 1> -> <RED: 1>
36 result = f"<{result[2:]}"
37 return result
38
39 if PY_311:
40
41 def __str__(self) -> str:
42 # We differentiate with standard Python flags in that we use a more descriptive str representation
43 # Standard Python flags just use the integer value as str, we use FlagName.ValueName
44 # In case of anonymous flags, we just use the ValueName
45 base = f"{self.__class__.__name__}." if self.__class__.__name__ else ""
46 return f"{base}{self.name}"
47
48 else:
49
50 def __str__(self) -> str:
51 result = IntFlag.__str__(self)
52 if not self.__class__.__name__:
53 # Deal with anonymous flags
54 # I.e. .RED -> RED
55 result = result[1:]
56 return result
57
58 def __eq__(self, other: int | Flag) -> bool:
59 if isinstance(other, Flag) and other.__class__ is not self.__class__:
60 return False
61
62 # Python <= 3.10 compatibility
63 if isinstance(other, Flag):
64 other = other.value
65
66 return self.value == other
67
68 def __ne__(self, value: int | Flag) -> bool:
69 return not self.__eq__(value)
70
71 def __hash__(self) -> int:
72 return hash((self.__class__, self.name, self.value))