This patch handles QAPI command types and generates data structures in
Go that handles it.

Note that command's id is part of the first layer of unmarshal, so it
is a member of protocol.go's Message type.

qapi:
 | ##
 | # @add-fd:
 | #
 | # Add a file descriptor, that was passed via SCM rights, to an fd set.
 | #
 | # @fdset-id: The ID of the fd set to add the file descriptor to.
 | #
 | # @opaque: A free-form string that can be used to describe the fd.
 | #
 | # Returns:
 | #     @AddfdInfo
 | #
 | # Errors:
 | #     - If file descriptor was not received, GenericError
 | #     - If @fdset-id is a negative value, GenericError
 | #
 | # .. note:: The list of fd sets is shared by all monitor connections.
 | #
 | # .. note:: If @fdset-id is not specified, a new fd set will be
 | #    created.
 | #
 | # Since: 1.2
 | #
 | # .. qmp-example::
 | #
 | #     -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } }
 | #     <- { "return": { "fdset-id": 1, "fd": 3 } }
 | ##
 | { 'command': 'add-fd',
 |   'data': { '*fdset-id': 'int',
 |             '*opaque': 'str' },
 |   'returns': 'AddfdInfo' }

go:
 | // Add a file descriptor, that was passed via SCM rights, to an fd
 | // set.
 | //
 | // Returns:   @AddfdInfo
 | //
 | // Errors:   - If file descriptor was not received, GenericError   -
 | // If @fdset-id is a negative value, GenericError
 | //
 | // .. note:: The list of fd sets is shared by all monitor connections.
 | // .. note:: If @fdset-id is not specified, a new fd set will be
 | // created.
 | //
 | // Since: 1.2
 | //
 | // .. qmp-example::    -> { "execute": "add-fd", "arguments": {
 | // "fdset-id": 1 } }   <- { "return": { "fdset-id": 1, "fd": 3 } }
 | type AddFdCommand struct {
 |      // The ID of the fd set to add the file descriptor to.
 |      FdsetId *int64 `json:"fdset-id,omitempty"`
 |      // A free-form string that can be used to describe the fd.
 |      Opaque *string `json:"opaque,omitempty"`
 | }

Signed-off-by: Victor Toso <victort...@redhat.com>
---
 scripts/qapi/golang/golang.py | 52 +++++++++++++++++++++++++++++++++--
 1 file changed, 50 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi/golang/golang.py b/scripts/qapi/golang/golang.py
index b9a2c47137..a14970fb1f 100644
--- a/scripts/qapi/golang/golang.py
+++ b/scripts/qapi/golang/golang.py
@@ -316,7 +316,7 @@ def qapi_to_go_type_name(name: str, meta: Optional[str] = 
None) -> str:
     name += "".join(word.title() for word in words[1:])
 
     # Handle specific meta suffix
-    types = ["event"]
+    types = ["event", "command"]
     if meta in types:
         name = name[:-3] if name.endswith("Arg") else name
         name += meta.title().replace(" ", "")
@@ -1009,6 +1009,15 @@ def generate_template_alternate(
     return "\n" + content
 
 
+def generate_template_command(commands: dict[str, Tuple[str, str]]) -> str:
+    content = ""
+    for name in sorted(commands):
+        type_name, gocode = commands[name]
+        content += gocode
+
+    return content
+
+
 def generate_template_event(events: dict[str, Tuple[str, str]]) -> (str, str):
     content = ""
     methods = ""
@@ -1069,6 +1078,7 @@ def __init__(self, _: str):
         # Map each qapi type to the necessary Go imports
         types = {
             "alternate": ["encoding/json", "errors", "fmt"],
+            "command": [],
             "enum": [],
             "event": [],
             "struct": ["encoding/json"],
@@ -1080,6 +1090,7 @@ def __init__(self, _: str):
 
         self.schema: QAPISchema
         self.events: dict[str, Tuple[str, str]] = {}
+        self.commands: dict[str, Tuple[str, str]] = {}
         self.golang_package_name = "qapi"
         self.duplicate = list(gofiles)
         self.enums: dict[str, str] = {}
@@ -1140,6 +1151,8 @@ def visit_end(self) -> None:
         self.types["event"] += evtype
         self.interfaces["event"] += eviface
 
+        self.types["command"] += generate_template_command(self.commands)
+
     def visit_object_type(
         self,
         name: str,
@@ -1286,7 +1299,42 @@ def visit_command(
         allow_preconfig: bool,
         coroutine: bool,
     ) -> None:
-        pass
+        assert name == info.defn_name
+        assert name not in self.commands
+
+        type_name = qapi_to_go_type_name(name, info.defn_meta)
+
+        doc = self.docmap.get(name, None)
+        type_doc, _ = qapi_to_golang_struct_docs(doc)
+
+        content = ""
+        if boxed or not arg_type or not qapi_name_is_object(arg_type.name):
+            args: List[dict[str:str]] = []
+            if arg_type:
+                args.append(
+                    {
+                        "name": f"{arg_type.name}",
+                    }
+                )
+            content += string_to_code(
+                generate_struct_type(type_name, type_doc=type_doc, args=args)
+            )
+        else:
+            assert isinstance(arg_type, QAPISchemaObjectType)
+            content += string_to_code(
+                qapi_to_golang_struct(
+                    self,
+                    name,
+                    arg_type.info,
+                    arg_type.ifcond,
+                    arg_type.features,
+                    arg_type.base,
+                    arg_type.members,
+                    arg_type.branches,
+                )
+            )
+
+        self.commands[name] = (type_name, content)
 
     def visit_event(
         self,
-- 
2.48.1


Reply via email to