[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v1 7/9] qapi: golang: Generate qapi's command types in Go
From: |
Victor Toso |
Subject: |
[PATCH v1 7/9] qapi: golang: Generate qapi's command types in Go |
Date: |
Wed, 27 Sep 2023 13:25:42 +0200 |
This patch handles QAPI command types and generates data structures in
Go that decodes from QMP JSON Object to Go data structure and vice
versa.
Similar to Event, this patch adds a Command interface and two helper
functions MarshalCommand and UnmarshalCommand.
Example:
qapi:
| { 'command': 'set_password',
| 'boxed': true,
| 'data': 'SetPasswordOptions' }
go:
| type SetPasswordCommand struct {
| SetPasswordOptions
| CommandId string `json:"-"`
| }
usage:
| input := `{"execute":"set_password",` +
| `"arguments":{"protocol":"vnc",` +
| `"password":"secret"}}`
|
| c, err := UnmarshalCommand([]byte(input))
| if err != nil {
| panic(err)
| }
|
| if c.GetName() == `set_password` {
| m := c.(*SetPasswordCommand)
| // m.Password == "secret"
| }
Signed-off-by: Victor Toso <victortoso@redhat.com>
---
scripts/qapi/golang.py | 97 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 94 insertions(+), 3 deletions(-)
diff --git a/scripts/qapi/golang.py b/scripts/qapi/golang.py
index ff3b1dd020..52a9124641 100644
--- a/scripts/qapi/golang.py
+++ b/scripts/qapi/golang.py
@@ -246,6 +246,51 @@
}}
'''
+TEMPLATE_COMMAND_METHODS = '''
+func (c *{type_name}) GetName() string {{
+ return "{name}"
+}}
+
+func (s *{type_name}) GetId() string {{
+ return s.MessageId
+}}
+'''
+
+TEMPLATE_COMMAND = '''
+type Command interface {{
+ GetId() string
+ GetName() string
+}}
+
+func MarshalCommand(c Command) ([]byte, error) {{
+ m := make(map[string]any)
+ m["execute"] = c.GetName()
+ if id := c.GetId(); len(id) > 0 {{
+ m["id"] = id
+ }}
+ if bytes, err := json.Marshal(c); err != nil {{
+ return []byte{{}}, err
+ }} else if len(bytes) > 2 {{
+ m["arguments"] = c
+ }}
+ return json.Marshal(m)
+}}
+
+func UnmarshalCommand(data []byte) (Command, error) {{
+ base := struct {{
+ MessageId string `json:"id,omitempty"`
+ Name string `json:"execute"`
+ }}{{}}
+ if err := json.Unmarshal(data, &base); err != nil {{
+ return nil, fmt.Errorf("Failed to decode command: %s", string(data))
+ }}
+
+ switch base.Name {{
+ {cases}
+ }}
+ return nil, errors.New("Failed to recognize command")
+}}
+'''
def gen_golang(schema: QAPISchema,
output_dir: str,
@@ -282,7 +327,7 @@ def qapi_to_go_type_name(name: str,
name += ''.join(word.title() for word in words[1:])
- types = ["event"]
+ types = ["event", "command"]
if meta in types:
name = name[:-3] if name.endswith("Arg") else name
name += meta.title().replace(" ", "")
@@ -521,6 +566,8 @@ def qapi_to_golang_struct(self: QAPISchemaGenGolangVisitor,
fields, with_nullable = recursive_base(self, base)
if info.defn_meta == "event":
fields += f'''\tMessageTimestamp Timestamp `json:"-"`\n{fields}'''
+ elif info.defn_meta == "command":
+ fields += f'''\tMessageId string `json:"-"`\n{fields}'''
if members:
for member in members:
@@ -719,16 +766,36 @@ def generate_template_event(events: dict[str, str]) ->
str:
'''
return TEMPLATE_EVENT.format(cases=cases)
+def generate_template_command(commands: dict[str, str]) -> str:
+ cases = ""
+ for name in sorted(commands):
+ case_type = commands[name]
+ cases += f'''
+case "{name}":
+ command := struct {{
+ Args {case_type} `json:"arguments"`
+ }}{{}}
+
+ if err := json.Unmarshal(data, &command); err != nil {{
+ return nil, fmt.Errorf("Failed to unmarshal: %s", string(data))
+ }}
+ command.Args.MessageId = base.MessageId
+ return &command.Args, nil
+'''
+ content = TEMPLATE_COMMAND.format(cases=cases)
+ return content
+
class QAPISchemaGenGolangVisitor(QAPISchemaVisitor):
def __init__(self, _: str):
super().__init__()
- types = ["alternate", "enum", "event", "helper", "struct", "union"]
+ types = ["alternate", "command", "enum", "event", "helper", "struct",
"union"]
self.target = {name: "" for name in types}
self.objects_seen = {}
self.schema = None
self.events = {}
+ self.commands = {}
self.golang_package_name = "qapi"
self.accept_null_types = []
@@ -756,6 +823,7 @@ def visit_begin(self, schema):
def visit_end(self):
self.schema = None
self.target["event"] += generate_template_event(self.events)
+ self.target["command"] += generate_template_command(self.commands)
def visit_object_type(self: QAPISchemaGenGolangVisitor,
name: str,
@@ -853,7 +921,30 @@ def visit_command(self,
allow_oob: bool,
allow_preconfig: bool,
coroutine: bool) -> None:
- pass
+ assert name == info.defn_name
+
+ type_name = qapi_to_go_type_name(name, info.defn_meta)
+ self.commands[name] = type_name
+
+ content = ""
+ if boxed or not arg_type or not qapi_name_is_object(arg_type.name):
+ args = "" if not arg_type else "\n" + arg_type.name
+ args += '''\n\tMessageId string `json:"-"`'''
+ content = generate_struct_type(type_name, args)
+ else:
+ assert isinstance(arg_type, QAPISchemaObjectType)
+ content = qapi_to_golang_struct(self,
+ name,
+ arg_type.info,
+ arg_type.ifcond,
+ arg_type.features,
+ arg_type.base,
+ arg_type.members,
+ arg_type.variants)
+
+ content += TEMPLATE_COMMAND_METHODS.format(name=name,
+ type_name=type_name)
+ self.target["command"] += content
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
assert name == info.defn_name
--
2.41.0
[PATCH v1 4/9] qapi: golang: structs: Address 'null' members, Victor Toso, 2023/09/27
[PATCH v1 7/9] qapi: golang: Generate qapi's command types in Go,
Victor Toso <=
[PATCH v1 8/9] qapi: golang: Add CommandResult type to Go, Victor Toso, 2023/09/27
[PATCH v1 9/9] docs: add notes on Golang code generator, Victor Toso, 2023/09/27
Re: [PATCH v1 0/9] qapi-go: add generator for Golang interface, Victor Toso, 2023/09/27
Re: [PATCH v1 0/9] qapi-go: add generator for Golang interface, Daniel P . Berrangé, 2023/09/28