diff mbox series

[v4,01/11] qapi: golang: first level unmarshalling type

Message ID 20250214202944.69897-2-victortoso@redhat.com (mailing list archive)
State New
Headers show
Series [v4,01/11] qapi: golang: first level unmarshalling type | expand

Commit Message

Victor Toso Feb. 14, 2025, 8:29 p.m. UTC
This first patch introduces protocol.go. It introduces the Message Go
struct type that can unmarshall any QMP message.

It does not handle deeper than 1st layer of the JSON object, that is,
with:

 1. {
      "execute": "query-machines",
      "arguments": { "compat-props": true }
    }

 2. {
      "event": "BALLOON_CHANGE",
      "data": { "actual": 944766976 },
      "timestamp": {
        "seconds": 1267020223,
        "microseconds": 435656
      }
    }

We will be able to know it is a query-machine command or a
balloon-change event. Specific data type to handle arguments/data will
be introduced further in the series.

This patch also introduces the Visitor skeleton with a proper write()
function to copy-over the protocol.go to the target destination.

Note, you can execute any patch of this series with:

    python3 ./scripts/qapi-gen.py -o /tmp/out qapi/qapi-schema.json

Signed-off-by: Victor Toso <victortoso@redhat.com>
---
 scripts/qapi/golang/__init__.py |   0
 scripts/qapi/golang/golang.py   | 135 ++++++++++++++++++++++++++++++++
 scripts/qapi/golang/protocol.go |  48 ++++++++++++
 scripts/qapi/main.py            |   2 +
 4 files changed, 185 insertions(+)
 create mode 100644 scripts/qapi/golang/__init__.py
 create mode 100644 scripts/qapi/golang/golang.py
 create mode 100644 scripts/qapi/golang/protocol.go
diff mbox series

Patch

diff --git a/scripts/qapi/golang/__init__.py b/scripts/qapi/golang/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/scripts/qapi/golang/golang.py b/scripts/qapi/golang/golang.py
new file mode 100644
index 0000000000..333741b47b
--- /dev/null
+++ b/scripts/qapi/golang/golang.py
@@ -0,0 +1,135 @@ 
+"""
+Golang QAPI generator
+"""
+
+# Copyright (c) 2025 Red Hat Inc.
+#
+# Authors:
+#  Victor Toso <victortoso@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+# Just for type hint on self
+from __future__ import annotations
+
+import os, shutil
+from typing import List, Optional
+
+from ..schema import (
+    QAPISchema,
+    QAPISchemaBranches,
+    QAPISchemaEnumMember,
+    QAPISchemaFeature,
+    QAPISchemaIfCond,
+    QAPISchemaObjectType,
+    QAPISchemaObjectTypeMember,
+    QAPISchemaType,
+    QAPISchemaVariants,
+    QAPISchemaVisitor,
+)
+from ..source import QAPISourceInfo
+
+
+def gen_golang(schema: QAPISchema, output_dir: str, prefix: str) -> None:
+    vis = QAPISchemaGenGolangVisitor(prefix)
+    schema.visit(vis)
+    vis.write(output_dir)
+
+
+class QAPISchemaGenGolangVisitor(QAPISchemaVisitor):
+    # pylint: disable=too-many-arguments
+    def __init__(self, _: str):
+        super().__init__()
+        gofiles = ("protocol.go",)
+        self.schema: QAPISchema
+        self.golang_package_name = "qapi"
+        self.duplicate = list(gofiles)
+
+    def visit_begin(self, schema: QAPISchema) -> None:
+        self.schema = schema
+
+    def visit_end(self) -> None:
+        del self.schema
+
+    def visit_object_type(
+        self,
+        name: str,
+        info: Optional[QAPISourceInfo],
+        ifcond: QAPISchemaIfCond,
+        features: List[QAPISchemaFeature],
+        base: Optional[QAPISchemaObjectType],
+        members: List[QAPISchemaObjectTypeMember],
+        branches: Optional[QAPISchemaBranches],
+    ) -> None:
+        pass
+
+    def visit_alternate_type(
+        self,
+        name: str,
+        info: Optional[QAPISourceInfo],
+        ifcond: QAPISchemaIfCond,
+        features: List[QAPISchemaFeature],
+        variants: QAPISchemaVariants,
+    ) -> None:
+        pass
+
+    def visit_enum_type(
+        self,
+        name: str,
+        info: Optional[QAPISourceInfo],
+        ifcond: QAPISchemaIfCond,
+        features: List[QAPISchemaFeature],
+        members: List[QAPISchemaEnumMember],
+        prefix: Optional[str],
+    ) -> None:
+        pass
+
+    def visit_array_type(
+        self,
+        name: str,
+        info: Optional[QAPISourceInfo],
+        ifcond: QAPISchemaIfCond,
+        element_type: QAPISchemaType,
+    ) -> None:
+        pass
+
+    def visit_command(
+        self,
+        name: str,
+        info: Optional[QAPISourceInfo],
+        ifcond: QAPISchemaIfCond,
+        features: List[QAPISchemaFeature],
+        arg_type: Optional[QAPISchemaObjectType],
+        ret_type: Optional[QAPISchemaType],
+        gen: bool,
+        success_response: bool,
+        boxed: bool,
+        allow_oob: bool,
+        allow_preconfig: bool,
+        coroutine: bool,
+    ) -> None:
+        pass
+
+    def visit_event(
+        self,
+        name: str,
+        info: Optional[QAPISourceInfo],
+        ifcond: QAPISchemaIfCond,
+        features: List[QAPISchemaFeature],
+        arg_type: Optional[QAPISchemaObjectType],
+        boxed: bool,
+    ) -> None:
+        pass
+
+    def write(self, outdir: str) -> None:
+        godir = "go"
+        targetpath = os.path.join(outdir, godir)
+        os.makedirs(targetpath, exist_ok=True)
+
+        # Content to be copied over
+        srcdir = os.path.dirname(os.path.realpath(__file__))
+        for filename in self.duplicate:
+            srcpath = os.path.join(srcdir, filename)
+            dstpath = os.path.join(targetpath, filename)
+            shutil.copyfile(srcpath, dstpath)
diff --git a/scripts/qapi/golang/protocol.go b/scripts/qapi/golang/protocol.go
new file mode 100644
index 0000000000..4ff8d2f3fb
--- /dev/null
+++ b/scripts/qapi/golang/protocol.go
@@ -0,0 +1,48 @@ 
+/*
+ * Copyright 2025 Red Hat, Inc.
+ * SPDX-License-Identifier: MIT-0
+ *
+ * Authors:
+ *  Victor Toso <victortoso@redhat.com>
+ *  Daniel P. Berrange <berrange@redhat.com>
+ */
+package qapi
+
+import (
+	"encoding/json"
+	"time"
+)
+
+/* Union of data for command, response, error, or event,
+ * since when receiving we don't know upfront which we
+ * must deserialize */
+type Message struct {
+	QMP       *json.RawMessage `json:"QMP,omitempty"`
+	Execute   string           `json:"execute,omitempty"`
+	ExecOOB   string           `json:"exec-oob,omitempty"`
+	Event     string           `json:"event,omitempty"`
+	Error     *json.RawMessage `json:"error,omitempty"`
+	Return    *json.RawMessage `json:"return,omitempty"`
+	ID        string           `json:"id,omitempty"`
+	Timestamp *Timestamp       `json:"timestamp,omitempty"`
+	Data      *json.RawMessage `json:"data,omitempty"`
+	Arguments *json.RawMessage `json:"arguments,omitempty"`
+}
+
+type QAPIError struct {
+	Class       string `json:"class"`
+	Description string `json:"desc"`
+}
+
+func (err *QAPIError) Error() string {
+	return err.Description
+}
+
+type Timestamp struct {
+	Seconds      int `json:"seconds"`
+	MicroSeconds int `json:"microseconds"`
+}
+
+func (t *Timestamp) AsTime() time.Time {
+	return time.Unix(int64(t.Seconds), int64(t.MicroSeconds)*1000)
+}
diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py
index 324081b9fc..af315c1ad1 100644
--- a/scripts/qapi/main.py
+++ b/scripts/qapi/main.py
@@ -16,6 +16,7 @@ 
 from .error import QAPIError
 from .events import gen_events
 from .features import gen_features
+from .golang import golang
 from .introspect import gen_introspect
 from .schema import QAPISchema
 from .types import gen_types
@@ -55,6 +56,7 @@  def generate(schema_file: str,
     gen_commands(schema, output_dir, prefix, gen_tracing)
     gen_events(schema, output_dir, prefix)
     gen_introspect(schema, output_dir, prefix, unmask)
+    golang.gen_golang(schema, output_dir, prefix)
 
 
 def main() -> int: