forked from COVESA/vss-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvspec2graphql.py
executable file
·129 lines (112 loc) · 4.34 KB
/
vspec2graphql.py
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
#!/usr/bin/env python3
# Copyright (c) 2021 COVESA Alliance
# Copyright (c) 2021 Motius GmbH
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
# Convert vspec file to graphql. Derived from vspec2protobuf converter.
#
# NOTE: This uses
# input files where all instantiations have been done. In other words,
# similar to the protobuf approach it is taking a simple approach and
# defines a new type for every instance, which is probably not required
# in a smarter schema.
import sys
import os
#Add path to main py vspec parser
myDir= os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(myDir, ".."))
import vspec
import getopt
from anytree import PreOrderIter
from vspec.model.vsstree import VSSNode
# For the initial graphql examplewe had all types mapped to a non-specified size,
# e.g. "Int" so this is following that example. I am not sure if this
# can/should be changed later.
mapped = {
"uint64": "Int",
"uint32": "Int",
"uint16": "Int",
"uint8": "Int",
"int64": "Int",
"int32": "Int",
"int16": "Int",
"int8": "Int",
"float": "Float",
"boolean": "Boolean",
"string": "String"
}
# Currently we only have types that are indented one level so this is very
# crude. For a more advanced generator, this might keep track of the
# current indentation level...
indent = ' '
def traverse_tree(tree, graphql_file):
tree_node: VSSNode
for tree_node in filter(lambda n: n.is_branch(), PreOrderIter(tree)):
if len(tree_node.children) == 0:
print(f"WARNING: Skipping branch with no children: {tree_node.qualified_name('.')}")
else:
graphql_file.write(indent + '"""\n')
graphql_file.write(indent + tree_node.description + "\n")
graphql_file.write(indent + '"""\n')
graphql_file.write(indent + f"type {tree_node.qualified_name('_')} {{" + "\n")
print_message_body(tree_node.children, graphql_file)
graphql_file.write(indent + "}\n\n")
def print_message_body(nodes, graphql_file):
for i, node in enumerate(nodes, 1):
data_type = node.qualified_name('_')
if not node.is_branch():
dt_val = node.data_type.value
data_type = mapped.get(dt_val.strip("[]"), dt_val.strip("[]"))
if data_type is None:
print(f"WARNING: Could not map type {{dt_val}}!\n")
if dt_val.endswith("[]"): # Array type
data_type = f"[{data_type}]"
if node.is_branch() and len(node.children) == 0:
pass # Don't generate a member variable if its (branch) type is empty
else:
# Not sure if it matters but the example file we had, had
# CamelCase but starting lower case
name = node.name[0].lower() + node.name[1:]
graphql_file.write(indent * 2 + '"""\n')
graphql_file.write(indent * 2 + node.description + "\n")
graphql_file.write(indent * 2 + '"""\n')
graphql_file.write(indent * 2 + f"{name}: {data_type}\n\n")
def usage():
print(
"""Usage: vspec2graphql.py [-I include_dir] vspec_file output_file
-I include_dir Add include directory to search for included vspec
files. Can be used multiple times.
vspec_file The vehicle specification file to parse.
output_file The file to write graphql schema to.)
"""
)
sys.exit(255)
if __name__ == "__main__":
#
# Check that we have the correct arguments
#
opts, args = getopt.getopt(sys.argv[1:], "I:i:")
# Always search current directory for include_file
include_dirs = ["."]
for o, a in opts:
if o == "-I":
include_dirs.append(a)
else:
usage()
if len(args) != 2:
usage()
graphql_file = open(args[1], "w")
graphql_file.write("import { gql } from 'apollo-server';\n\n")
graphql_file.write("const Vehicle = gql`\n")
try:
tree = vspec.load_tree(args[0], include_dirs)
traverse_tree(tree, graphql_file)
except vspec.VSpecError as e:
print("Error: {}".format(e))
exit(255)
graphql_file.write("`;\n")
graphql_file.write("export default Vehicle;\n")
graphql_file.close()