Paolo Bonzini <[email protected]> writes:

> From: Marc-AndrĂ© Lureau <[email protected]>
>
> Generate high-level native Rust declarations for the QAPI types.
>
> - 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 <[email protected]>
> Link: 
> https://lore.kernel.org/r/[email protected]
> Signed-off-by: Paolo Bonzini <[email protected]>

I like to look at the generated code before I look at the code
generator.  My go-to schema for a first look is example-schema.json from
docs/devel/qapi-code-gen.rst:

    { 'struct': 'UserDefOne',
      'data': { 'integer': 'int', '*string': 'str', '*flag': 'bool' } }

    { 'command': 'my-command',
      'data': { 'arg1': ['UserDefOne'] },
      'returns': 'UserDefOne' }

    { 'event': 'MY_EVENT' }

Generated example-qapi-types.rs:

    // @generated by qapi-gen, DO NOT EDIT

    #![allow(unexpected_cfgs)]
    #![allow(non_camel_case_types)]
    #![allow(clippy::empty_structs_with_brackets)]
    #![allow(clippy::large_enum_variant)]
    #![allow(clippy::pub_underscore_fields)]

    // Because QAPI structs can contain float, for simplicity we never
    // derive Eq.  Clippy however would complain for those structs
    // that *could* be Eq too.
    #![allow(clippy::derive_partial_eq_without_eq)]

    use util::qobject::QObject;


    #[repr(u32)]
    #[derive(Copy, Clone, Debug, PartialEq, common::TryInto)]
    pub enum QType {

        NONE,

        QNULL,

        QNUM,

        QSTRING,

        QDICT,

        QLIST,

        QBOOL,

        _MAX,
    }


    impl Default for QType {
        #[inline]
        fn default() -> QType {
            Self::NONE
        }
    }


    #[derive(Clone, Debug, PartialEq)]
    pub struct UserDefOne {

        pub integer: i64,

        pub string: Option<String>,

        pub flag: Option<bool>,
    }

Questions / observations:

* Why is util::qobject::QObject needed?

* NONE is an error value, not a valid QType.  Having such error values
  in enums isn't unusual in C.  What about idiomatic Rust?  Even if it's
  unusual there, we may elect to do it anyway, just to keep generated
  Rust closer to C.  But it should be a conscious decision, not a blind
  port from C to Rust.

* "Default for QType" is NONE.  In C, it's zero bytes, which boils down
  to QTYPE_NONE.

* QTYPE__MAX is a bit of a headache in C.  It's not a valid enum value.
  We make it one only because we need to know the largest valid enum
  value, e.g. to size arrays, and the easiest way to get that value is
  adding an invalid one to the enum.  Same for all the other generated
  enums.  Could we avoid it in Rust?

* Blank lines before the values of enum QType and the members of struct
  UserDefOne contain spaces.  PATCH 16 will remove the spaces.

* Definitions are separated by two blank lines.  PATCH 16 will collapse
  them into one.

Compare to example-qapi-types.h:

    /* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */

    /*
     * Schema-defined QAPI types
     *
     * Copyright IBM, Corp. 2011
     * Copyright (c) 2013-2018 Red Hat Inc.
     *
     * This work is licensed under the terms of the GNU LGPL, version 2.1 or 
later.
     * See the COPYING.LIB file in the top-level directory.
     */

    #ifndef EXAMPLE_QAPI_TYPES_H
    #define EXAMPLE_QAPI_TYPES_H

    #include "qapi/qapi-builtin-types.h"

    typedef struct UserDefOne UserDefOne;

    typedef struct UserDefOneList UserDefOneList;

    typedef struct q_obj_my_command_arg q_obj_my_command_arg;

    struct UserDefOne {
        int64_t integer;
        char *string;
        bool has_flag;
        bool flag;
    };

    void qapi_free_UserDefOne(UserDefOne *obj);
    G_DEFINE_AUTOPTR_CLEANUP_FUNC(UserDefOne, qapi_free_UserDefOne)

    struct UserDefOneList {
        UserDefOneList *next;
        UserDefOne *value;
    };

    void qapi_free_UserDefOneList(UserDefOneList *obj);
    G_DEFINE_AUTOPTR_CLEANUP_FUNC(UserDefOneList, qapi_free_UserDefOneList)

    struct q_obj_my_command_arg {
        UserDefOneList *arg1;
    };

    #endif /* EXAMPLE_QAPI_TYPES_H */

Observations:

* C has a file comment of the form

    /*
     * One-line description of the file's purpose
     *
     * Copyright lines
     *
     * License blurb
     */

  I think Rust could use such a comment, too.

* C has built-in types like QType in qapi-builtin-types.h, generated
  only with -b.  This is a somewhat crude way to let code generated for
  multiple schemas coexist: pass -b for exactly one of them.  If we
  generated code for built-in types unconditionally into qapi-types.h,
  the C compiler would choke on duplicate definitions.  Why is this not
  a problem with Rust?

* The Rust version doesn't have deallocation boilerplate.  Deallocation
  just works there, I guess.

* The Rust version doesn't have the List type.  Lists just work there, I
  guess.

* The Rust version doesn't have the implicit type q_obj_my_command_arg,
  which is the arguments of my-command as a struct type.  C needs it for
  marshaling / unmarshaling with visitors.  Rust doesn't, because we use
  serde.  Correct?


Reply via email to