[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC PATCH v2 6/8] qapi: golang: Generate qapi's command types in Go
From: |
Victor Toso |
Subject: |
[RFC PATCH v2 6/8] qapi: golang: Generate qapi's command types in Go |
Date: |
Fri, 17 Jun 2022 14:19:30 +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.
Simlar to Event, this patch adds a Command interface and two helper
functions MarshalCommand and UnmarshalCommand.
At the time of this writing, it generates 209 structures.
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 | 123 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 120 insertions(+), 3 deletions(-)
diff --git a/scripts/qapi/golang.py b/scripts/qapi/golang.py
index b2e08cebdf..123179cced 100644
--- a/scripts/qapi/golang.py
+++ b/scripts/qapi/golang.py
@@ -88,6 +88,63 @@
return nil, errors.New("Failed to recognize event")
}}
'''
+
+# Only variable is @unm_cases to handle all command's names and associated
types.
+TEMPLATE_COMMAND = '''
+type Command interface {{
+ GetId() string
+ GetName() string
+ GetReturnType() CommandReturn
+}}
+
+func MarshalCommand(c Command) ([]byte, error) {{
+ baseStruct := struct {{
+ CommandId string `json:"id,omitempty"`
+ Name string `json:"execute"`
+ }}{{
+ CommandId: c.GetId(),
+ Name: c.GetName(),
+ }}
+ base, err := json.Marshal(baseStruct)
+ if err != nil {{
+ return []byte{{}}, err
+ }}
+
+ argsStruct := struct {{
+ Args Command `json:"arguments,omitempty"`
+ }}{{
+ Args: c,
+ }}
+ args, err := json.Marshal(argsStruct)
+ if err != nil {{
+ return []byte{{}}, err
+ }}
+
+ if len(args) == len(`{{"arguments":{{}}}}`) {{
+ return base, nil
+ }}
+
+ // Combines Event's base and data in a single JSON object
+ result := fmt.Sprintf("%s,%s", base[:len(base)-1], args[1:])
+ return []byte(result), nil
+}}
+
+func UnmarshalCommand(data []byte) (Command, error) {{
+ base := struct {{
+ CommandId string `json:"id,omitempty"`
+ Name string `json:"execute"`
+ }}{{}}
+ if err := json.Unmarshal(data, &base); err != nil {{
+ return nil, errors.New(fmt.Sprintf("Failed to decode command: %s",
string(data)))
+ }}
+
+ switch base.Name {{
+ {unm_cases}
+ }}
+ return nil, errors.New("Failed to recognize command")
+}}
+'''
+
TEMPLATE_HELPER = '''
// Alias for go version lower than 1.18
type Any = interface{}
@@ -112,12 +169,13 @@ class QAPISchemaGenGolangVisitor(QAPISchemaVisitor):
def __init__(self, prefix: str):
super().__init__()
- self.target = {name: "" for name in ["alternate", "enum",
+ self.target = {name: "" for name in ["alternate", "command", "enum",
"event", "helper", "struct",
"union"]}
self.objects_seen = {}
self.schema = None
self.events = {}
+ self.commands = {}
self.golang_package_name = "qapi"
def visit_begin(self, schema):
@@ -149,6 +207,23 @@ def visit_end(self):
'''
self.target["event"] += TEMPLATE_EVENT.format(unm_cases=unm_cases)
+ unm_cases = ""
+ for name in sorted(self.commands):
+ case_type = self.commands[name]
+ unm_cases += f'''
+ case "{name}":
+ command := struct {{
+ Args {case_type} `json:"arguments"`
+ }}{{}}
+
+ if err := json.Unmarshal(data, &command); err != nil {{
+ return nil, errors.New(fmt.Sprintf("Failed to unmarshal: %s",
string(data)))
+ }}
+ command.Args.CommandId = base.CommandId
+ return &command.Args, nil
+'''
+ self.target["command"] += TEMPLATE_COMMAND.format(unm_cases=unm_cases)
+
def visit_object_type(self: QAPISchemaGenGolangVisitor,
name: str,
@@ -308,7 +383,47 @@ def visit_command(self,
allow_oob: bool,
allow_preconfig: bool,
coroutine: bool) -> None:
- pass
+ # Safety check
+ assert name == info.defn_name
+
+ type_name = qapi_to_go_type_name(name, info.defn_meta)
+ self.commands[name] = type_name
+ command_ret = ""
+ init_ret_type_name = f'''EmptyCommandReturn {{ Name: "{name}" }}'''
+
+ self_contained = True
+ if arg_type and arg_type.name.startswith("q_obj"):
+ self_contained = False
+
+ content = ""
+ if boxed or self_contained:
+ args = "" if not arg_type else "\n" + arg_type.name
+ args += '''\n\tCommandId string `json:"-"`'''
+ content = generate_struct_type(type_name, args)
+ else:
+ assert isinstance(arg_type, QAPISchemaObjectType)
+ content = qapi_to_golang_struct(name,
+ arg_type.info,
+ arg_type.ifcond,
+ arg_type.features,
+ arg_type.base,
+ arg_type.members,
+ arg_type.variants)
+
+ methods = f'''
+func (c *{type_name}) GetName() string {{
+ return "{name}"
+}}
+
+func (s *{type_name}) GetId() string {{
+ return s.CommandId
+}}
+
+func (s *{type_name}) GetReturnType() CommandReturn {{
+ return &{init_ret_type_name}
+}}
+'''
+ self.target["command"] += content + methods
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
assert name == info.defn_name
@@ -385,6 +500,8 @@ def qapi_to_golang_struct(name: str,
fields = ""
if info.defn_meta == "event":
fields += '''\tEventTimestamp Timestamp `json:"-"`\n'''
+ elif info.defn_meta == "command":
+ fields += '''\tCommandId string `json:"-"`\n'''
if base:
base_fields = ""
@@ -569,7 +686,7 @@ def qapi_to_go_type_name(name: str, meta: str) -> str:
name += ''.join(word.title() for word in words[1:])
- if meta in ["event"]:
+ if meta in ["event", "command"]:
name = name[:-3] if name.endswith("Arg") else name
name += meta.title().replace(" ", "")
--
2.36.1
- [RFC PATCH v2 0/8] qapi: add generator for Golang interface, Victor Toso, 2022/06/17
- [RFC PATCH v2 2/8] qapi: golang: Generate qapi's alternate types in Go, Victor Toso, 2022/06/17
- [RFC PATCH v2 3/8] qapi: golang: Generate qapi's struct types in Go, Victor Toso, 2022/06/17
- [RFC PATCH v2 4/8] qapi: golang: Generate qapi's union types in Go, Victor Toso, 2022/06/17
- [RFC PATCH v2 5/8] qapi: golang: Generate qapi's event types in Go, Victor Toso, 2022/06/17
- [RFC PATCH v2 6/8] qapi: golang: Generate qapi's command types in Go,
Victor Toso <=
- [RFC PATCH v2 8/8] qapi: golang: document skip function visit_array_types, Victor Toso, 2022/06/17
- [RFC PATCH v2 7/8] qapi: golang: Add CommandResult type to Go, Victor Toso, 2022/06/17
- [RFC PATCH v2 1/8] qapi: golang: Generate qapi's enum types in Go, Victor Toso, 2022/06/17
- Re: [RFC PATCH v2 0/8] qapi: add generator for Golang interface, Markus Armbruster, 2022/06/27