qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Zhao Liu <zhao1.liu@intel.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-devel@nongnu.org, marcandre.lureau@redhat.com,
	qemu-rust@nongnu.org, armbru@redhat.com, mkletzan@redhat.com
Subject: Re: [PATCH 3/3] scripts/qapi: generate high-level Rust bindings
Date: Wed, 11 Jun 2025 16:09:40 +0800	[thread overview]
Message-ID: <aEk5xDG+kcr3NEok@intel.com> (raw)
In-Reply-To: <20250605101124.367270-4-pbonzini@redhat.com>

On Thu, Jun 05, 2025 at 12:11:24PM +0200, Paolo Bonzini wrote:
> Date: Thu,  5 Jun 2025 12:11:24 +0200
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 3/3] scripts/qapi: generate high-level Rust bindings
> X-Mailer: git-send-email 2.49.0
> 
> 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.

Why not map the C union to rust union directly (in `pub enum %(rs_name)sVariant`)?

(latter comments are all about format nits)

...

> +%(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

This introduces extra \n, which will generate a blank line if there's
no cfg.

> +    %(rs_name)sVariant::%(var_name)s%(patt)s => Self::%(var_name)s,

So, I think it should be:

    %(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 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,

ditto,

    %(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)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)
> +
> +    return ret
> +
> +
> +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_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)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
> +
> +

...

> +%(cfg)s
> +#[repr(u32)]
> +#[derive(Copy, Clone, Debug, PartialEq, Eq, qemu_api_macros::TryInto)]
> +pub enum %(rs_name)s {
> +''',
> +                cfg=ifcond.rsgen(),
> +                rs_name=rs_name(name))
> +
> +    for member in enum_members:
> +        ret += mcgen('''
> +    %(cfg)s
> +    %(c_enum)s,

    %(cfg)s    %(c_enum)s,

> +''',
> +                     cfg=member.ifcond.rsgen(),
> +                     c_enum=to_camel_case(rs_name(member.name)))
> +    # picked the first, since that's what malloc0 does
> +    # but arguably could use _MAX instead, or a qapi annotation
> +    default = to_camel_case(rs_name(enum_members[0].name))
> +    ret += mcgen('''
> +}
> +

...

> +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)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)))
> +
> +    ret += mcgen('''
> +}
> +''')
> +    return ret


  reply	other threads:[~2025-06-11  7:48 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-05 10:11 [PATCH preview 0/3] reviving minimal QAPI generation from 2021 Paolo Bonzini
2025-06-05 10:11 ` [PATCH 1/3] rust: make TryFrom macro more resilient Paolo Bonzini
2025-06-10 13:26   ` Marc-André Lureau
2025-06-10 15:52   ` Zhao Liu
2025-06-05 10:11 ` [PATCH 2/3] scripts/qapi: add QAPISchemaIfCond.rsgen() Paolo Bonzini
2025-06-10 13:33   ` Marc-André Lureau
2025-06-05 10:11 ` [PATCH 3/3] scripts/qapi: generate high-level Rust bindings Paolo Bonzini
2025-06-11  8:09   ` Zhao Liu [this message]
2025-06-10 13:53 ` [PATCH preview 0/3] reviving minimal QAPI generation from 2021 Marc-André Lureau
2025-06-10 16:10   ` Paolo Bonzini
2025-06-11  8:13 ` Zhao Liu
2025-06-11  8:57   ` Paolo Bonzini
2025-06-12 10:24     ` Paolo Bonzini
2025-06-13  5:57       ` Zhao Liu
2025-06-16  8:55         ` Paolo Bonzini
2025-06-18 14:25       ` Markus Armbruster
2025-06-18 17:36         ` Paolo Bonzini
2025-06-23 12:52           ` Markus Armbruster
2025-07-02 19:09             ` Paolo Bonzini
2025-06-17  7:49 ` Markus Armbruster
2025-06-18  8:27   ` Paolo Bonzini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aEk5xDG+kcr3NEok@intel.com \
    --to=zhao1.liu@intel.com \
    --cc=armbru@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=mkletzan@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-rust@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).