summaryrefslogtreecommitdiff
path: root/scripts/generate_schema_enums.py
blob: 13fbefe40f10af3ff4861b56d0cfd717c1a02aa0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/python3
import os
import xml.etree.ElementTree as ET
from collections import defaultdict
import shutil
import re


SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
REDFISH_SCHEMA_DIR = os.path.realpath(
    os.path.join(SCRIPT_DIR, "..", "static", "redfish", "v1", "schema")
)

OUTFOLDER = os.path.realpath(os.path.join(
    SCRIPT_DIR,
    "..",
    "redfish-core",
    "include",
    "generated",
    "enums"
  )
)

# Odata string types
EDMX = "{http://docs.oasis-open.org/odata/ns/edmx}"
EDM = "{http://docs.oasis-open.org/odata/ns/edm}"


class Enum:
    def __init__(self, name, values, namespace, from_file):
        self.name = name
        self.values = values
        self.namespace = namespace
        self.from_file = from_file


def parse_schema(element, filename):
    EntityTypes = []
    namespace = element.attrib["Namespace"]
    for schema_element in element:
        name = schema_element.attrib.get("Name", None)
        if name is None:
            continue
        if schema_element.tag == EDM + "EnumType":
            enums = []
            for member in schema_element.findall(EDM + "Member"):
                enums.append(member.attrib["Name"])
            EntityTypes.append(Enum(name, enums, namespace, filename))
    return EntityTypes


def parse_file(filename):
    tree = ET.parse(filename)
    root = tree.getroot()
    results = []
    data_services = root.findall(EDMX + "DataServices")
    for ds in data_services:
        for element in ds:
            if element.tag == EDM + "Schema":
                results.extend(parse_schema(element, filename))

    return results


def camel_to_snake(name):
    # snake casing PCIeDevice and PCIeFunction results in mediocre results
    # given that the standard didn't camel case those in a way that the
    # algorithm expects, so change the casing explicitly to generate sane
    # snake case results.
    name = name.replace("PCIe", "Pcie")
    name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
    return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower()


def write_enum_list(redfish_defs_file, enum_list, snake_case_namespace):
    redfish_defs_file.write(
        "#pragma once\n"
        "#include <nlohmann/json.hpp>\n\n"
        "namespace {}\n"
        "{{\n"
        "// clang-format off\n\n".format(snake_case_namespace)
    )

    for element in enum_list:
        redfish_defs_file.write("enum class {}{{\n".format(element.name))
        values = element.values
        if "Invalid" not in values:
            values.insert(0, "Invalid")

        for value in values:
            redfish_defs_file.write("    {},\n".format(value))

        redfish_defs_file.write("};\n\n")

    for element in enum_list:
        values = element.values
        if "Invalid" not in values:
            values.insert(0, "Invalid")
        # nlohmann::json aparently uses c style arrays in their enum
        # implementation, and clang-tidy isn't smart enough to figure out that
        # the C arrays are in their code not bmcwebs, so we have to explicitly
        # ignore the error.
        redfish_defs_file.write(
            "NLOHMANN_JSON_SERIALIZE_ENUM({}, {{\n".format(
                element.name
            )
        )
        for value in values:
            redfish_defs_file.write(
                '    {{{}::{}, "{}"}},\n'.format(element.name, value, value)
            )

        redfish_defs_file.write("});\n\n")

        print(element.name)

    redfish_defs_file.write("}\n" "// clang-format on\n")


def generate_enums(flat_list):
    # clear out the old results if they exist
    if os.path.exists(OUTFOLDER):
        shutil.rmtree(OUTFOLDER)
    os.makedirs(OUTFOLDER)

    enum_by_namespace = defaultdict(list)

    for element in flat_list:
        if isinstance(element, Enum):
            namespace_split = element.namespace.split(".")[0]
            enum_by_namespace[namespace_split].append(element)

    for namespace, enum_list in enum_by_namespace.items():
        snake_case_namespace = camel_to_snake(namespace)
        outfile = os.path.join(
            OUTFOLDER, "{}.hpp".format(snake_case_namespace)
        )

        with open(outfile, "w") as redfish_defs:
            write_enum_list(redfish_defs, enum_list, snake_case_namespace)


def main():
    print("Reading from {}".format(REDFISH_SCHEMA_DIR))
    dir_list = os.listdir(REDFISH_SCHEMA_DIR)

    filepaths = [
        os.path.join(REDFISH_SCHEMA_DIR, filename) for filename in dir_list
    ]

    enum_list = []

    for filepath in filepaths:
        out = parse_file(filepath)
        enum_list.extend(out)

    print("Parsing done")

    generate_enums(enum_list)


if __name__ == "__main__":
    main()