qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC v3 20/32] scripts/qapi: generate high-level Rust bindings


From: marcandre . lureau
Subject: [RFC v3 20/32] scripts/qapi: generate high-level Rust bindings
Date: Tue, 7 Sep 2021 16:19:31 +0400

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Generate high-level idiomatic Rust code for the QAPI types, with to/from
translations for the C FFI.

- char* is mapped to String, scalars to there corresponding Rust types

- enums are simply aliased from FFI

- has_foo/foo members are mapped to Option<T>

- lists are represented as Vec<T>

- structures have Rust versions, with To/From FFI conversions

- alternate are represented as Rust enum

- unions are represented in a similar way as in C: a struct S with a "u"
  member (since S may have extra 'base' fields). However, the discriminant
  isn't a member of S, since Rust enum already include it.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 meson.build              |   1 +
 scripts/qapi/main.py     |   2 +
 scripts/qapi/rs.py       |  94 +++-
 scripts/qapi/rs_types.py | 966 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 1062 insertions(+), 1 deletion(-)
 create mode 100644 scripts/qapi/rs_types.py

diff --git a/meson.build b/meson.build
index 74e90059c2..8e12a4dd70 100644
--- a/meson.build
+++ b/meson.build
@@ -2017,6 +2017,7 @@ qapi_gen_depends = [ meson.source_root() / 
'scripts/qapi/__init__.py',
                      meson.source_root() / 'scripts/qapi/common.py',
                      meson.source_root() / 'scripts/qapi/rs.py',
                      meson.source_root() / 'scripts/qapi/rs_ffi.py',
+                     meson.source_root() / 'scripts/qapi/rs_types.py',
                      meson.source_root() / 'scripts/qapi-gen.py',
 ]
 
diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py
index deba72ee4e..9756c0c35d 100644
--- a/scripts/qapi/main.py
+++ b/scripts/qapi/main.py
@@ -17,6 +17,7 @@
 from .events import gen_events
 from .introspect import gen_introspect
 from .rs_ffi import gen_rs_ffitypes
+from .rs_types import gen_rs_types
 from .schema import QAPISchema
 from .types import gen_types
 from .visit import gen_visit
@@ -52,6 +53,7 @@ def generate(schema_file: str,
     schema = QAPISchema(schema_file)
     if rust:
         gen_rs_ffitypes(schema, output_dir, prefix)
+        gen_rs_types(schema, output_dir, prefix)
     else:
         gen_types(schema, output_dir, prefix, builtins)
         gen_visit(schema, output_dir, prefix, builtins)
diff --git a/scripts/qapi/rs.py b/scripts/qapi/rs.py
index be42329fa4..b53930eab2 100644
--- a/scripts/qapi/rs.py
+++ b/scripts/qapi/rs.py
@@ -9,7 +9,7 @@
 import subprocess
 from typing import NamedTuple, Optional
 
-from .common import POINTER_SUFFIX
+from .common import POINTER_SUFFIX, mcgen
 from .gen import QAPIGen
 from .schema import QAPISchemaModule, QAPISchemaVisitor
 
@@ -53,6 +53,64 @@ def rs_name(name: str, protect: bool = True) -> str:
     return name
 
 
+def rs_type(c_type: str,
+            qapi_ns: Optional[str] = 'qapi::',
+            optional: Optional[bool] = False,
+            box: bool = False) -> str:
+    (is_pointer, _, is_list, c_type) = rs_ctype_parse(c_type)
+    # accepts QAPI types ('any', 'str', ...) as we translate
+    # qapiList to Rust FFI types here.
+    to_rs = {
+        'any': 'QObject',
+        'bool': 'bool',
+        'char': 'i8',
+        'double': 'f64',
+        'int': 'i64',
+        'int16': 'i16',
+        'int16_t': 'i16',
+        'int32': 'i32',
+        'int32_t': 'i32',
+        'int64': 'i64',
+        'int64_t': 'i64',
+        'int8': 'i8',
+        'int8_t': 'i8',
+        'null': 'QNull',
+        'number': 'f64',
+        'size': 'u64',
+        'str': 'String',
+        'uint16': 'u16',
+        'uint16_t': 'u16',
+        'uint32': 'u32',
+        'uint32_t': 'u32',
+        'uint64': 'u64',
+        'uint64_t': 'u64',
+        'uint8': 'u8',
+        'uint8_t': 'u8',
+        'String': 'QapiString',
+    }
+    if is_pointer:
+        to_rs.update({
+            'char': 'String',
+        })
+
+    if is_list:
+        c_type = c_type[:-4]
+
+    to_rs = to_rs.get(c_type)
+    if to_rs:
+        ret = to_rs
+    else:
+        ret = qapi_ns + c_type
+
+    if is_list:
+        ret = 'Vec<%s>' % ret
+    elif is_pointer and not to_rs and box:
+        ret = 'Box<%s>' % ret
+    if optional:
+        ret = 'Option<%s>' % ret
+    return ret
+
+
 class CType(NamedTuple):
     is_pointer: bool
     is_const: bool
@@ -140,6 +198,40 @@ def to_snake_case(value: str) -> str:
     return snake_case.sub(r'_\1', value).lower()
 
 
+def to_qemu_none(c_type: str, name: str) -> str:
+    (is_pointer, _, is_list, _) = rs_ctype_parse(c_type)
+
+    if is_pointer:
+        if c_type == 'char':
+            return mcgen('''
+    let %(name)s_ = CString::new(%(name)s).unwrap();
+    let %(name)s = %(name)s_.as_ptr();
+''', name=name)
+        if is_list:
+            return mcgen('''
+    let %(name)s_ = NewPtr(%(name)s).to_qemu_none();
+    let %(name)s = %(name)s_.0.0;
+''', name=name)
+        return mcgen('''
+    let %(name)s_ = %(name)s.to_qemu_none();
+    let %(name)s = %(name)s_.0;
+''', name=name)
+    return ''
+
+
+def from_qemu(var_name: str, c_type: str, full: Optional[bool] = False) -> str:
+    (is_pointer, _, is_list, c_type) = rs_ctype_parse(c_type)
+    ptr = '{} as *{} _'.format(var_name, 'mut' if full else 'const')
+    if is_list:
+        ptr = 'NewPtr({})'.format(ptr)
+    if is_pointer:
+        ret = 'from_qemu_{}({})'.format('full' if full else 'none', ptr)
+        if c_type != 'char' and not is_list:
+            ret = 'Box::new(%s)' % ret
+        return ret
+    return var_name
+
+
 class QAPIGenRs(QAPIGen):
     pass
 
diff --git a/scripts/qapi/rs_types.py b/scripts/qapi/rs_types.py
new file mode 100644
index 0000000000..eb9877a0de
--- /dev/null
+++ b/scripts/qapi/rs_types.py
@@ -0,0 +1,966 @@
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+"""
+QAPI Rust types generator
+"""
+
+from typing import List, Optional
+
+from .common import POINTER_SUFFIX, mcgen
+from .rs import (
+    QAPISchemaRsVisitor,
+    from_qemu,
+    rs_ctype_parse,
+    rs_ffitype,
+    rs_name,
+    rs_type,
+    to_camel_case,
+    to_snake_case,
+)
+from .schema import (
+    QAPISchema,
+    QAPISchemaEnumMember,
+    QAPISchemaEnumType,
+    QAPISchemaFeature,
+    QAPISchemaIfCond,
+    QAPISchemaObjectType,
+    QAPISchemaObjectTypeMember,
+    QAPISchemaType,
+    QAPISchemaVariants,
+)
+from .source import QAPISourceInfo
+
+
+objects_seen = set()
+
+
+def gen_rs_variants_to_tag(name: str,
+                           ifcond: QAPISchemaIfCond,
+                           variants: Optional[QAPISchemaVariants]) -> str:
+    ret = mcgen('''
+
+%(cfg)s
+impl From<&%(rs_name)sVariant> for %(tag)s {
+    fn from(e: &%(rs_name)sVariant) -> Self {
+        match e {
+    ''',
+                cfg=ifcond.rsgen(),
+                rs_name=rs_name(name),
+                tag=rs_type(variants.tag_member.type.c_type(), ''))
+
+    for var in variants.variants:
+        type_name = var.type.name
+        var_name = to_camel_case(rs_name(var.name))
+        patt = '(_)'
+        if type_name == 'q_empty':
+            patt = ''
+        ret += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(var_name)s%(patt)s => Self::%(var_name)s,
+''',
+                     cfg=var.ifcond.rsgen(),
+                     rs_name=rs_name(name),
+                     var_name=var_name,
+                     patt=patt)
+
+    ret += mcgen('''
+        }
+    }
+}
+''')
+    return ret
+
+
+def variants_to_qemu_inner(name: str,
+                           variants: Optional[QAPISchemaVariants]) -> str:
+    members = ''
+    none_arms = ''
+    full_arms = ''
+    lifetime = ''
+    for var in variants.variants:
+        var_name = to_camel_case(rs_name(var.name))
+        type_name = var.type.name
+        if type_name == 'q_empty':
+            members += mcgen('''
+    %(cfg)s
+    %(var_name)s,
+''',
+                             cfg=var.ifcond.rsgen(),
+                             var_name=var_name)
+            none_arms += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(var_name)s => {
+        (std::ptr::null_mut(),
+         %(rs_name)sVariantStorage::%(var_name)s)
+    },
+''',
+                               cfg=var.ifcond.rsgen(),
+                               rs_name=rs_name(name),
+                               var_name=var_name)
+            full_arms += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(var_name)s => {
+        std::ptr::null_mut()
+    }
+''',
+                               cfg=var.ifcond.rsgen(),
+                               rs_name=rs_name(name),
+                               var_name=var_name)
+            continue
+        c_type = var.type.c_unboxed_type()
+        if type_name.endswith('-wrapper'):
+            wrap = list(var.type.members)[0]
+            type_name = wrap.type.name
+            c_type = wrap.type.c_unboxed_type()
+
+        lifetime = "<'a>"
+        (_, _, is_list, ffitype) = rs_ctype_parse(c_type)
+        ffitype = rs_ffitype(ffitype)
+        ptr_ty = 'NewPtr<*mut %s>' % ffitype if is_list else '*mut ' + ffitype
+        stash_ty = ': Stash<%s, _>' % ptr_ty if is_list else ''
+
+        members += mcgen('''
+    %(cfg)s
+    %(var_name)s(<%(rs_type)s as ToQemuPtr<'a, %(ptr_ty)s>>::Storage),
+''',
+                         cfg=var.ifcond.rsgen(),
+                         var_name=var_name,
+                         rs_type=rs_type(c_type, ''),
+                         ptr_ty=ptr_ty)
+        none_arms += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(var_name)s(v) => {
+        let stash_%(stash_ty)s = v.to_qemu_none();
+        (stash_.0.to() as *mut std::ffi::c_void,
+         %(rs_name)sVariantStorage::%(var_name)s(stash_.1))
+    },
+''',
+                           cfg=var.ifcond.rsgen(),
+                           rs_name=rs_name(name),
+                           var_name=var_name,
+                           stash_ty=stash_ty)
+        ptr_ty = ': %s' % ptr_ty if is_list else ''
+        full_arms += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(var_name)s(v) => {
+        let ptr%(ptr_ty)s = v.to_qemu_full();
+        ptr.to() as *mut std::ffi::c_void
+    },
+''',
+                           cfg=var.ifcond.rsgen(),
+                           rs_name=rs_name(name),
+                           var_name=var_name,
+                           ptr_ty=ptr_ty)
+    return (members, none_arms, full_arms, lifetime)
+
+
+def gen_rs_variants_to_qemu(name: str,
+                            ifcond: QAPISchemaIfCond,
+                            variants: Optional[QAPISchemaVariants]) -> str:
+    (members, none_arms, full_arms, lifetime) = \
+        variants_to_qemu_inner(name, variants)
+    return mcgen('''
+
+%(cfg)s
+impl QemuPtrDefault for %(rs_name)sVariant {
+    type QemuType = *mut std::ffi::c_void;
+}
+
+%(cfg)s
+pub enum %(rs_name)sVariantStorage%(lt)s {
+    %(members)s
+}
+
+%(cfg)s
+impl<'a> ToQemuPtr<'a, *mut std::ffi::c_void> for %(rs_name)sVariant {
+    type Storage = %(rs_name)sVariantStorage%(lt)s;
+
+    #[inline]
+    fn to_qemu_none(&'a self)
+    -> Stash<'a, *mut std::ffi::c_void, %(rs_name)sVariant> {
+        let (ptr_, cenum_) = match self {
+             %(none_arms)s
+        };
+
+        Stash(ptr_, cenum_)
+    }
+
+    #[inline]
+    fn to_qemu_full(&self) -> *mut std::ffi::c_void {
+        match self {
+            %(full_arms)s
+        }
+    }
+}
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name),
+                 lt=lifetime,
+                 members=members,
+                 none_arms=none_arms,
+                 full_arms=full_arms)
+
+
+def gen_rs_variants(name: str,
+                    ifcond: QAPISchemaIfCond,
+                    variants: Optional[QAPISchemaVariants]) -> str:
+    ret = mcgen('''
+
+%(cfg)s
+#[derive(Clone,Debug)]
+pub enum %(rs_name)sVariant {
+''',
+                cfg=ifcond.rsgen(),
+                rs_name=rs_name(name))
+
+    for var in variants.variants:
+        type_name = var.type.name
+        var_name = to_camel_case(rs_name(var.name, False))
+        if type_name == 'q_empty':
+            ret += mcgen('''
+    %(cfg)s
+    %(var_name)s,
+''',
+                         cfg=var.ifcond.rsgen(),
+                         var_name=var_name)
+        else:
+            c_type = var.type.c_unboxed_type()
+            if c_type.endswith('_wrapper'):
+                c_type = c_type[6:-8]  # remove q_obj*-wrapper
+            ret += mcgen('''
+    %(cfg)s
+    %(var_name)s(%(rs_type)s),
+''',
+                         cfg=var.ifcond.rsgen(),
+                         var_name=var_name,
+                         rs_type=rs_type(c_type, ''))
+
+    ret += mcgen('''
+}
+''')
+
+    ret += gen_rs_variants_to_tag(name, ifcond, variants)
+    # implement ToQemu trait for the storage handling
+    # no need for gen_rs_variants_from_qemu() however
+    ret += gen_rs_variants_to_qemu(name, ifcond, variants)
+
+    return ret
+
+
+def gen_rs_object_to_qemu(name: str,
+                          ifcond: QAPISchemaIfCond,
+                          base: Optional[QAPISchemaObjectType],
+                          members: List[QAPISchemaObjectTypeMember],
+                          variants: Optional[QAPISchemaVariants]) -> str:
+    storage = []
+    stash = []
+    ffi_memb = []
+    memb_none = ''
+    memb_full = ''
+    if base:
+        members = list(base.members) + members
+    for memb in members:
+        if variants and variants.tag_member.name == memb.name:
+            continue
+        memb_name = to_snake_case(rs_name(memb.name))
+        c_type = memb.type.c_type()
+        (is_pointer, _, is_list, _) = rs_ctype_parse(c_type)
+        if is_pointer:
+            if memb.ifcond.is_present():
+                raise NotImplementedError("missing support for condition here")
+            typ = rs_type(memb.type.c_type(),
+                          optional=memb.optional,
+                          qapi_ns='',
+                          box=True)
+            styp = rs_ffitype(memb.type.c_type(), list_as_newp=True)
+            storage.append("Stash<'a, %s, %s>" % (styp, typ))
+        if memb.optional:
+            has_memb_name = 'has_%s' % rs_name(memb.name, protect=False)
+            ffi_memb.append(f"{memb.ifcond.rsgen()} {has_memb_name}")
+            has_memb = mcgen('''
+    %(cfg)s
+    let %(has_memb_name)s = self.%(memb_name)s.is_some();
+''',
+                             cfg=memb.ifcond.rsgen(),
+                             memb_name=memb_name,
+                             has_memb_name=has_memb_name)
+            memb_none += has_memb
+            memb_full += has_memb
+
+        if is_pointer:
+            stash_name = '{}_stash_'.format(memb_name)
+            stash.append(stash_name)
+            var = 'NewPtr(%s)' % memb_name if is_list else memb_name
+            memb_none += mcgen('''
+    let %(stash_name)s = self.%(memb_name)s.to_qemu_none();
+    let %(var)s = %(stash_name)s.0;
+''', stash_name=stash_name, memb_name=memb_name, var=var)
+            memb_full += mcgen('''
+    let %(var)s = self.%(memb_name)s.to_qemu_full();
+''', memb_name=memb_name, var=var)
+        else:
+            unwrap = ''
+            if memb.optional:
+                unwrap = '.unwrap_or_default()'
+            assign = mcgen('''
+    %(cfg)s
+    let %(memb_name)s = self.%(memb_name)s%(unwrap)s;
+''',
+                           cfg=memb.ifcond.rsgen(),
+                           memb_name=memb_name,
+                           unwrap=unwrap)
+            memb_none += assign
+            memb_full += assign
+
+        ffi_memb.append(f"{memb.ifcond.rsgen()} {memb_name}")
+
+    if variants:
+        tag_name = rs_name(variants.tag_member.name)
+        ffi_memb.append(tag_name)
+        ffi_memb.append('u')
+        voidp = '*mut std::ffi::c_void'
+        storage.append("Stash<'a, %s, %sVariant>" % (voidp, rs_name(name)))
+        tag = mcgen('''
+    let %(tag_name)s = (&self.u).into();
+''', tag_name=tag_name)
+        memb_none += tag
+        memb_full += tag
+        arms_none = ''
+        arms_full = ''
+        for variant in variants.variants:
+            typ = variant.type
+            if typ.name == 'q_empty':
+                arms_none += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariantStorage::%(kind)s => qapi_ffi::%(rs_name)sUnion {
+        qapi_dummy: qapi_ffi::QapiDummy,
+    },''',
+                                   cfg=variant.ifcond.rsgen(),
+                                   rs_name=rs_name(name),
+                                   kind=to_camel_case(rs_name(variant.name)))
+                arms_full += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(kind)s => qapi_ffi::%(rs_name)sUnion {
+        qapi_dummy: qapi_ffi::QapiDummy,
+    },''',
+                                   cfg=variant.ifcond.rsgen(),
+                                   rs_name=rs_name(name),
+                                   kind=to_camel_case(rs_name(variant.name)))
+            else:
+                if typ.name.endswith('-wrapper'):
+                    wrap_ty = list(typ.members)[0].type.c_type()
+                    ptr = wrap_ty.endswith(POINTER_SUFFIX)
+                    val = (
+                        rs_ffitype(variant.type.c_unboxed_type()) +
+                        ' { data: u_stash_.0.to() as *mut _ }' if ptr else
+                        ' { data: unsafe { *(u_stash_.0.to() as *const _) } }'
+                    )
+                else:
+                    val = '*_s.0'
+                arms_none += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariantStorage::%(kind)s(ref _s) => qapi_ffi::%(rs_name)sUnion {
+        %(var_name)s: %(val)s,
+    },''',
+                                   cfg=variant.ifcond.rsgen(),
+                                   rs_name=rs_name(name),
+                                   kind=to_camel_case(rs_name(variant.name)),
+                                   var_name=rs_name(variant.name),
+                                   val=val)
+                arms_full += mcgen('''
+    %(cfg)s
+    %(rs_name)sVariant::%(kind)s(_) => qapi_ffi::%(rs_name)sUnion {
+        %(var_name)s: *(u_ptr_.to() as *const _),
+    },''',
+                                   cfg=variant.ifcond.rsgen(),
+                                   rs_name=rs_name(name),
+                                   kind=to_camel_case(rs_name(variant.name)),
+                                   var_name=rs_name(variant.name))
+        memb_none += mcgen('''
+    let u_stash_ = self.u.to_qemu_none();
+    let u = match u_stash_.1 {
+        %(arms)s
+    };
+''', arms=arms_none)
+        stash.append('u_stash_')
+        memb_full += mcgen('''
+    let u_ptr_ = self.u.to_qemu_full();
+    let u = match self.u {
+        %(arms)s
+    };
+    ffi::g_free(u_ptr_);
+''', arms=arms_full)
+
+    if not ffi_memb:
+        ffi_memb = ['qapi_dummy_for_empty_struct: 0']
+
+    return mcgen('''
+
+%(cfg)s
+impl QemuPtrDefault for %(rs_name)s {
+    type QemuType = *mut qapi_ffi::%(rs_name)s;
+}
+
+%(cfg)s
+impl<'a> ToQemuPtr<'a, *mut qapi_ffi::%(rs_name)s> for %(rs_name)s {
+    type Storage = (Box<qapi_ffi::%(rs_name)s>, %(storage)s);
+
+    #[inline]
+    fn to_qemu_none(&'a self)
+    -> Stash<'a, *mut qapi_ffi::%(rs_name)s, %(rs_name)s> {
+        %(memb_none)s
+        let mut box_ = Box::new(qapi_ffi::%(rs_name)s { %(ffi_memb)s });
+
+        Stash(&mut *box_, (box_, %(stash)s))
+    }
+
+    #[inline]
+    fn to_qemu_full(&self) -> *mut qapi_ffi::%(rs_name)s {
+        unsafe {
+            %(memb_full)s
+            let ptr = ffi::g_malloc0(
+                std::mem::size_of::<%(rs_name)s>()) as *mut _;
+            *ptr = qapi_ffi::%(rs_name)s { %(ffi_memb)s };
+            ptr
+        }
+    }
+}
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name),
+                 storage=', '.join(storage),
+                 ffi_memb=', '.join(ffi_memb),
+                 memb_none=memb_none,
+                 memb_full=memb_full,
+                 stash=', '.join(stash))
+
+
+def gen_rs_members(members: List[QAPISchemaObjectTypeMember],
+                   exclude: List[str] = None) -> str:
+    exclude = exclude or []
+    return [f"{m.ifcond.rsgen()} {to_snake_case(rs_name(m.name))}"
+            for m in members if m.name not in exclude]
+
+
+def gen_rs_object_from_qemu(name: str,
+                            ifcond: QAPISchemaIfCond,
+                            base: Optional[QAPISchemaObjectType],
+                            members: List[QAPISchemaObjectTypeMember],
+                            variants: Optional[QAPISchemaVariants]) -> str:
+    exclude = [variants.tag_member.name] if variants else []
+    memb_names = []
+    if base:
+        names = gen_rs_members(base.members, exclude)
+        memb_names.extend(names)
+    names = gen_rs_members(members, exclude)
+    memb_names.extend(names)
+
+    ret = mcgen('''
+
+%(cfg)s
+impl FromQemuPtrFull<*mut qapi_ffi::%(rs_name)s> for %(rs_name)s {
+    unsafe fn from_qemu_full(ffi: *mut qapi_ffi::%(rs_name)s) -> Self {
+        let ret = from_qemu_none(ffi as *const _);
+        qapi_ffi::qapi_free_%(name)s(ffi);
+        ret
+    }
+}
+
+%(cfg)s
+impl FromQemuPtrNone<*const qapi_ffi::%(rs_name)s> for %(rs_name)s {
+    unsafe fn from_qemu_none(ffi: *const qapi_ffi::%(rs_name)s) -> Self {
+        let _ffi = &*ffi;
+''',
+                cfg=ifcond.rsgen(),
+                name=rs_name(name, protect=False),
+                rs_name=rs_name(name))
+
+    if base:
+        members = list(base.members) + members
+
+    tag_member = variants.tag_member if variants else None
+    for memb in members:
+        if memb == tag_member:
+            continue
+        memb_name = rs_name(memb.name)
+        val = from_qemu('_ffi.' + to_snake_case(memb_name), memb.type.c_type())
+        if memb.optional:
+            val = mcgen('''{
+            if _ffi.has_%(memb_name)s {
+                Some(%(val)s)
+            } else {
+                None
+            }
+}''',
+                        memb_name=rs_name(memb.name, protect=False),
+                        val=val)
+
+        ret += mcgen('''
+        %(cfg)s
+        let %(snake_memb_name)s = %(val)s;
+''',
+                     cfg=memb.ifcond.rsgen(),
+                     snake_memb_name=to_snake_case(memb_name),
+                     memb_name=memb_name,
+                     val=val)
+
+    if variants:
+        arms = ''
+        assert isinstance(variants.tag_member.type, QAPISchemaEnumType)
+        for variant in variants.variants:
+            typ = variant.type
+            if typ.name == 'q_empty':
+                memb = ''
+            else:
+                ptr = True
+                is_list = False
+                memb = to_snake_case(rs_name(variant.name))
+                if typ.name.endswith('-wrapper'):
+                    memb = '_ffi.u.%s.data' % memb
+                    wrap_ty = list(typ.members)[0].type.c_type()
+                    (ptr, _, is_list, _) = rs_ctype_parse(wrap_ty)
+                else:
+                    memb = '&_ffi.u.%s' % memb
+                if ptr:
+                    memb = '%s as *const _' % memb
+                    if is_list:
+                        memb = 'NewPtr(%s)' % memb
+                    memb = 'from_qemu_none(%s)' % memb
+                memb = '(%s)' % memb
+            arms += mcgen('''
+%(cfg)s
+%(enum)s::%(variant)s => { %(rs_name)sVariant::%(variant)s%(memb)s },
+''',
+                          cfg=variant.ifcond.rsgen(),
+                          enum=rs_name(variants.tag_member.type.name),
+                          memb=memb,
+                          variant=to_camel_case(rs_name(variant.name)),
+                          rs_name=rs_name(name))
+        ret += mcgen('''
+        let u = match _ffi.%(tag)s {
+            %(arms)s
+            _ => panic!("Variant with invalid tag"),
+        };
+''',
+                     tag=rs_name(variants.tag_member.name),
+                     arms=arms)
+        memb_names.append('u')
+
+    ret += mcgen('''
+            Self { %(memb_names)s }
+        }
+}
+''',
+                 memb_names=', '.join(memb_names))
+    return ret
+
+
+def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
+    ret = ''
+    for memb in members:
+        typ = rs_type(memb.type.c_type(), '', optional=memb.optional, box=True)
+        ret += mcgen('''
+    %(cfg)s
+    pub %(rs_name)s: %(rs_type)s,
+''',
+                     cfg=memb.ifcond.rsgen(),
+                     rs_type=typ,
+                     rs_name=to_snake_case(rs_name(memb.name)))
+    return ret
+
+
+def gen_rs_object(name: str,
+                  ifcond: QAPISchemaIfCond,
+                  base: Optional[QAPISchemaObjectType],
+                  members: List[QAPISchemaObjectTypeMember],
+                  variants: Optional[QAPISchemaVariants]) -> str:
+    if name in objects_seen:
+        return ''
+
+    if variants:
+        members = [m for m in members
+                   if m.name != variants.tag_member.name]
+
+    ret = ''
+    objects_seen.add(name)
+
+    if variants:
+        ret += gen_rs_variants(name, ifcond, variants)
+
+    ret += mcgen('''
+
+%(cfg)s
+#[derive(Clone, Debug)]
+pub struct %(rs_name)s {
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name))
+
+    if base:
+        if not base.is_implicit():
+            ret += mcgen('''
+    // Members inherited:
+''',
+                         c_name=base.c_name())
+        base_members = base.members
+        if variants:
+            base_members = [m for m in base.members
+                            if m.name != variants.tag_member.name]
+        ret += gen_struct_members(base_members)
+        if not base.is_implicit():
+            ret += mcgen('''
+    // Own members:
+''')
+
+    ret += gen_struct_members(members)
+
+    if variants:
+        ret += mcgen('''
+    pub u: %(rs_type)sVariant,
+''', rs_type=rs_name(name))
+    ret += mcgen('''
+}
+''')
+
+    ret += gen_rs_object_from_qemu(name, ifcond, base, members, variants)
+    ret += gen_rs_object_to_qemu(name, ifcond, base, members, variants)
+    return ret
+
+
+def gen_rs_alternate_from_qemu(name: str,
+                               ifcond: QAPISchemaIfCond,
+                               variants: Optional[QAPISchemaVariants]) -> str:
+    arms = ''
+    for var in variants.variants:
+        qtype = to_camel_case(var.type.alternate_qtype()[6:].lower())
+        ptr = var.type.c_unboxed_type().endswith(POINTER_SUFFIX)
+        memb = 'ffi.u.%s' % rs_name(var.name)
+        if not ptr:
+            memb = '&' + memb
+        arms += mcgen('''
+        %(cfg)s
+        QType::%(qtype)s => {
+            Self::%(kind)s(from_qemu_none(%(memb)s as *const _))
+        }
+''',
+                      qtype=qtype,
+                      cfg=var.ifcond.rsgen(),
+                      kind=to_camel_case(rs_name(var.name)),
+                      memb=memb)
+
+    ret = mcgen('''
+
+%(cfg)s
+impl FromQemuPtrFull<*mut qapi_ffi::%(rs_name)s> for %(rs_name)s {
+    unsafe fn from_qemu_full(ffi: *mut qapi_ffi::%(rs_name)s) -> Self {
+        let ret = from_qemu_none(ffi as *const _);
+        qapi_ffi::qapi_free_%(name)s(ffi);
+        ret
+    }
+}
+
+%(cfg)s
+impl FromQemuPtrNone<*const qapi_ffi::%(rs_name)s> for %(rs_name)s {
+    unsafe fn from_qemu_none(ffi: *const qapi_ffi::%(rs_name)s) -> Self {
+        let ffi = &*ffi;
+
+        match ffi.r#type {
+            %(arms)s
+            _ => panic!()
+        }
+    }
+}
+''',
+                cfg=ifcond.rsgen(),
+                name=rs_name(name, protect=False),
+                rs_name=rs_name(name),
+                arms=arms)
+    return ret
+
+
+def gen_rs_alternate_to_qemu(name: str,
+                             ifcond: QAPISchemaIfCond,
+                             variants: Optional[QAPISchemaVariants],
+                             lifetime: str) -> str:
+    arms_none = ''
+    arms_full = ''
+    for var in variants.variants:
+        if var.type.name == 'q_empty':
+            continue
+        ptr = var.type.c_unboxed_type().endswith(POINTER_SUFFIX)
+        val = 'val.0' if ptr else 'unsafe { *(val.0.to() as *const _) }'
+        stor = '(val.1)' if var.type.c_type().endswith(POINTER_SUFFIX) else ''
+        qtype = var.type.alternate_qtype()[6:].lower()
+        arms_none += mcgen('''
+        %(cfg)s
+        Self::%(memb_name)s(val) => {
+            let val = val.to_qemu_none();
+            (
+                QType::%(qtype)s,
+                qapi_ffi::%(rs_name)sUnion { %(ffi_memb_name)s: %(val)s },
+                %(rs_name)sStorage::%(memb_name)s%(stor)s
+            )
+        }
+''',
+                           rs_name=rs_name(name),
+                           cfg=var.ifcond.rsgen(),
+                           memb_name=to_camel_case(rs_name(var.name)),
+                           ffi_memb_name=rs_name(var.name),
+                           qtype=to_camel_case(qtype),
+                           val=val,
+                           stor=stor)
+        val = 'val' if ptr else '*val'
+        free = '' if ptr else 'ffi::g_free(val as *mut _);'
+        arms_full += mcgen('''
+        %(cfg)s
+        Self::%(memb_name)s(val) => {
+            let val = val.to_qemu_full();
+            let ret = (QType::%(qtype)s, qapi_ffi::%(rs_name)sUnion {
+                %(ffi_memb_name)s: %(val)s
+            } );
+            %(free)s
+            ret
+        }
+''',
+                           rs_name=rs_name(name),
+                           cfg=var.ifcond.rsgen(),
+                           memb_name=to_camel_case(rs_name(var.name)),
+                           ffi_memb_name=rs_name(var.name),
+                           qtype=to_camel_case(qtype),
+                           val=val,
+                           free=free)
+
+    memb_none = mcgen('''
+    let (r#type, u, stor) = match self {
+        %(arms_none)s
+    };
+''', arms_none=arms_none)
+    memb_full = mcgen('''
+    let (r#type, u) = match self {
+        %(arms_full)s
+    };
+''', arms_full=arms_full)
+    ffi_memb = ['r#type', 'u']
+    return mcgen('''
+
+%(cfg)s
+impl QemuPtrDefault for %(rs_name)s {
+    type QemuType = *mut qapi_ffi::%(rs_name)s;
+}
+
+%(cfg)s
+impl<'a> ToQemuPtr<'a, *mut qapi_ffi::%(rs_name)s> for %(rs_name)s {
+    // Additional boxing of storage needed due to recursive types
+    type Storage = (Box<qapi_ffi::%(rs_name)s>, Box<%(rs_name)sStorage%(lt)s>);
+
+    #[inline]
+    fn to_qemu_none(&'a self)
+    -> Stash<'a, *mut qapi_ffi::%(rs_name)s, %(rs_name)s> {
+        %(memb_none)s
+        let mut box_ = Box::new(qapi_ffi::%(rs_name)s { %(ffi_memb)s });
+
+        Stash(&mut *box_, (box_, Box::new(stor)))
+    }
+
+    #[inline]
+    fn to_qemu_full(&self) -> *mut qapi_ffi::%(rs_name)s {
+        unsafe {
+            %(memb_full)s
+            let ptr = ffi::g_malloc0(
+                std::mem::size_of::<%(rs_name)s>()) as *mut _;
+            *ptr = qapi_ffi::%(rs_name)s { %(ffi_memb)s };
+            ptr
+        }
+    }
+}
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name),
+                 lt=lifetime,
+                 ffi_memb=', '.join(ffi_memb),
+                 memb_none=memb_none,
+                 memb_full=memb_full)
+
+
+def gen_rs_alternate(name: str,
+                     ifcond: QAPISchemaIfCond,
+                     variants: Optional[QAPISchemaVariants]) -> str:
+    if name in objects_seen:
+        return ''
+
+    ret = ''
+    objects_seen.add(name)
+
+    ret += mcgen('''
+%(cfg)s
+#[derive(Clone, Debug)]
+pub enum %(rs_name)s {
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name))
+
+    for var in variants.variants:
+        if var.type.name == 'q_empty':
+            continue
+        ret += mcgen('''
+        %(cfg)s
+        %(mem_name)s(%(rs_type)s),
+''',
+                     cfg=var.ifcond.rsgen(),
+                     rs_type=rs_type(var.type.c_unboxed_type(), ''),
+                     mem_name=to_camel_case(rs_name(var.name)))
+
+    membs = ''
+    lifetime = ''
+    for var in variants.variants:
+        var_name = to_camel_case(rs_name(var.name))
+        type_name = var.type.name
+        if type_name == 'q_empty':
+            continue
+        if not var.type.c_type().endswith(POINTER_SUFFIX):
+            membs += mcgen('''
+    %(cfg)s
+    %(var_name)s,
+''',
+                           cfg=var.ifcond.rsgen(),
+                           var_name=var_name)
+        else:
+            lifetime = "<'a>"
+            c_type = var.type.c_type()
+            ptr_ty = rs_ffitype(c_type)
+            membs += mcgen('''
+    %(cfg)s
+    %(var_name)s(<%(rs_type)s as ToQemuPtr<'a, %(ptr_ty)s>>::Storage),
+''',
+                           cfg=var.ifcond.rsgen(),
+                           var_name=var_name,
+                           rs_type=rs_type(c_type, ''),
+                           ptr_ty=ptr_ty)
+    ret += mcgen('''
+}
+
+%(cfg)s
+pub enum %(rs_name)sStorage%(lt)s {
+    %(membs)s
+}
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name),
+                 lt=lifetime,
+                 membs=membs)
+
+    ret += gen_rs_alternate_from_qemu(name, ifcond, variants)
+    ret += gen_rs_alternate_to_qemu(name, ifcond, variants, lifetime)
+
+    return ret
+
+
+def gen_rs_enum(name: str, ifcond: QAPISchemaIfCond) -> str:
+    return mcgen('''
+
+%(cfg)s
+pub type %(rs_name)s = qapi_ffi::%(ffi_name)s;
+
+%(cfg)s
+impl_to_qemu_scalar_boxed!(%(rs_name)s);
+
+%(cfg)s
+impl_from_qemu_none_scalar!(%(rs_name)s);
+''',
+                 cfg=ifcond.rsgen(),
+                 rs_name=rs_name(name),
+                 ffi_name=rs_name(name))
+
+
+class QAPISchemaGenRsTypeVisitor(QAPISchemaRsVisitor):
+
+    def __init__(self, prefix: str) -> None:
+        super().__init__(prefix, 'qapi-types')
+
+    def visit_begin(self, schema: QAPISchema) -> None:
+        # don't visit the empty type
+        objects_seen.add(schema.the_empty_object_type.name)
+        self._gen.preamble_add(
+            mcgen('''
+// generated by qapi-gen, DO NOT EDIT
+
+use common::{QNull, QObject};
+use crate::qapi_ffi;
+
+'''))
+
+    def visit_array_type(self,
+                         name: str,
+                         info: Optional[QAPISourceInfo],
+                         ifcond: QAPISchemaIfCond,
+                         element_type: QAPISchemaType) -> None:
+        typ = rs_type(name, qapi_ns='')
+        scalar = False
+        if name[:-4] in {'number',
+                         'int',
+                         'int8',
+                         'int16',
+                         'int32',
+                         'int64',
+                         'uint8',
+                         'uint16',
+                         'uint32',
+                         'uint64',
+                         'size',
+                         'bool'}:
+            scalar = True
+        if isinstance(element_type, QAPISchemaEnumType):
+            scalar = True
+
+        self._gen.add(mcgen('''
+%(cfg)s
+mod %(mod)s_module {
+    use super::*;
+
+    vec_type!(%(rs)s, %(ffi)s, qapi_free_%(name)s, %(scalar)i);
+}
+
+%(cfg)s
+pub use %(mod)s_module::*;
+''',
+                            cfg=ifcond.rsgen(),
+                            name=rs_name(name, protect=False),
+                            mod=rs_name(name).lower(),
+                            ffi=rs_name(name),
+                            rs=typ,
+                            scalar=scalar))
+
+    def visit_object_type(self,
+                          name: str,
+                          info: Optional[QAPISourceInfo],
+                          ifcond: QAPISchemaIfCond,
+                          features: List[QAPISchemaFeature],
+                          base: Optional[QAPISchemaObjectType],
+                          members: List[QAPISchemaObjectTypeMember],
+                          variants: Optional[QAPISchemaVariants]) -> None:
+        if name.startswith('q_'):
+            return
+        self._gen.add(gen_rs_object(name, ifcond, base, members, variants))
+
+    def visit_enum_type(self,
+                        name: str,
+                        info: Optional[QAPISourceInfo],
+                        ifcond: QAPISchemaIfCond,
+                        features: List[QAPISchemaFeature],
+                        members: List[QAPISchemaEnumMember],
+                        prefix: Optional[str]) -> None:
+        self._gen.add(gen_rs_enum(name, ifcond))
+
+    def visit_alternate_type(self,
+                             name: str,
+                             info: QAPISourceInfo,
+                             ifcond: QAPISchemaIfCond,
+                             features: List[QAPISchemaFeature],
+                             variants: QAPISchemaVariants) -> None:
+        self._gen.add(gen_rs_alternate(name, ifcond, variants))
+
+
+def gen_rs_types(schema: QAPISchema, output_dir: str, prefix: str) -> None:
+    vis = QAPISchemaGenRsTypeVisitor(prefix)
+    schema.visit(vis)
+    vis.write(output_dir)
-- 
2.33.0.113.g6c40894d24




reply via email to

[Prev in Thread] Current Thread [Next in Thread]