ritual 0.0.0

Automatic generator of C++ library wrappers
Documentation
use crate::cpp_data::CppClassField;
use crate::cpp_data::CppPath;
use crate::cpp_function::{CppFunction, ReturnValueAllocationPlace};
use crate::cpp_type::{CppFunctionPointerType, CppType};
use ritual_common::errors::Result;
use serde_derive::{Deserialize, Serialize};

/// Variation of a field accessor method
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub enum CppFieldAccessorType {
    /// Returns copy of the field
    CopyGetter,
    /// Returns const reference to the field
    ConstRefGetter,
    /// Returns mutable reference to the field
    MutRefGetter,
    /// Copies value from its argument to the field
    Setter,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum CppCast {
    Static {
        /// If true, this is an unsafe (from base to derived) `static_cast` wrapper.
        is_unsafe: bool,

        /// If Some, this is a wrapper of `static_cast` between a class and its
        /// direct base. Contains index of the base (e.g. 0 for the first base; always
        /// 0 if the class only has one base).
        direct_base_index: Option<usize>,
    },
    Dynamic,
    #[allow(unused)]
    QObject,
}

impl CppCast {
    pub fn cpp_method_name(&self) -> &'static str {
        match *self {
            CppCast::Static { .. } => "static_cast",
            CppCast::Dynamic => "dynamic_cast",
            CppCast::QObject => "qobject_cast",
        }
    }

    pub fn is_unsafe_static_cast(&self) -> bool {
        match *self {
            CppCast::Static { ref is_unsafe, .. } => *is_unsafe,
            _ => false,
        }
    }
    pub fn is_first_direct_static_cast(&self) -> bool {
        match *self {
            CppCast::Static {
                ref direct_base_index,
                ..
            } => direct_base_index == &Some(0),
            _ => false,
        }
    }
}

/// Information about real nature of a C++ FFI method.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
pub enum CppFfiFunctionKind {
    /// This is a real C++ function.
    Function {
        cpp_function: CppFunction,
        /// If `Some`, the method is derived from another method by omitting arguments,
        /// and this field contains the number of omitted arguments.
        omitted_arguments: Option<usize>,
        /// If Some, this is an instance of `static_cast`, `dynamic_cast` or
        /// `qobject_cast` function call.
        cast: Option<CppCast>,
    },
    /// This is a field accessor, i.e. a non-existing getter or setter
    /// method for a public field.
    FieldAccessor {
        /// Type of the accessor
        accessor_type: CppFieldAccessorType,
        // /// Name of the C++ field
        field: CppClassField,
    },
}

impl CppFfiFunctionKind {
    pub fn cpp_function(&self) -> Option<&CppFunction> {
        if let CppFfiFunctionKind::Function {
            ref cpp_function, ..
        } = *self
        {
            Some(cpp_function)
        } else {
            None
        }
    }
    pub fn cpp_field(&self) -> Option<&CppClassField> {
        if let CppFfiFunctionKind::FieldAccessor { ref field, .. } = *self {
            Some(field)
        } else {
            None
        }
    }
}

/// Relation between original C++ method's argument value
/// and corresponding FFI function's argument value
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum CppTypeConversionToFfi {
    /// Argument types are identical
    NoChange,
    /// C++ argument is a class value (like QPoint)
    /// and FFI argument is a pointer (like QPoint*)
    ValueToPointer,
    /// C++ argument is a reference (like QPoint&)
    /// and FFI argument is a pointer (like QPoint*)
    ReferenceToPointer,
    /// C++ argument is QFlags<T>
    /// and FFI argument is uint
    QFlagsToUInt,
}

/// Information that indicates how an FFI function argument
/// should be interpreted
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum CppFfiArgumentMeaning {
    /// This argument contains value for "this" pointer
    /// used to call C++ class member functions
    This,
    /// Value of this argument should be passed as an argument to
    /// the original C++ method. Associated value is index of the
    /// C++ method's argument (counting from 0).
    Argument(i8),
    /// This argument receives pointer to the buffer where
    /// the return value should be transferred to using placement new.
    ReturnValue,
}

impl CppFfiArgumentMeaning {
    /// Checks if this argument corresponds to an original
    /// C++ method's argument
    pub fn is_argument(&self) -> bool {
        match *self {
            CppFfiArgumentMeaning::Argument(..) => true,
            _ => false,
        }
    }
}

/// Representation of an argument of a FFI function
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CppFfiFunctionArgument {
    /// Identifier
    pub name: String,
    /// Type
    pub argument_type: CppFfiType,
    /// C++ equivalent
    pub meaning: CppFfiArgumentMeaning,
}

impl CppFfiFunctionArgument {
    /// Generates C++ code for the part of FFI function signature
    /// corresponding to this argument
    pub fn to_cpp_code(&self) -> Result<String> {
        if let CppType::FunctionPointer(..) = self.argument_type.ffi_type {
            Ok(self.argument_type.ffi_type.to_cpp_code(Some(&self.name))?)
        } else {
            Ok(format!(
                "{} {}",
                self.argument_type.ffi_type.to_cpp_code(None)?,
                self.name
            ))
        }
    }
}

/// Information about arguments and return type of a FFI function
/// with no final function name
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CppFfiFunction {
    /// List of arguments
    pub arguments: Vec<CppFfiFunctionArgument>,
    /// Return type
    pub return_type: CppFfiType,

    /// Allocation place method used for converting
    /// the return type of the method
    /// or used to determine implementation of the destructor
    pub allocation_place: ReturnValueAllocationPlace,

    /// Final name of FFI method
    pub path: CppPath,

    pub kind: CppFfiFunctionKind,
}

impl CppFfiFunction {
    /// Returns true if this signature has const this_ptr argument,
    /// indicating that original C++ method has const attribute.
    /// Returns false if there is no this argument or it's not const.
    pub fn has_const_this(&self) -> bool {
        self.arguments.iter().any(|arg| {
            arg.meaning == CppFfiArgumentMeaning::This
                && match arg.argument_type.ffi_type {
                    CppType::PointerLike { is_const, .. } => is_const,
                    _ => false,
                }
        })
    }

    pub fn short_text(&self) -> String {
        match self.kind {
            CppFfiFunctionKind::Function {
                ref cpp_function,
                ref omitted_arguments,
                ..
            } => {
                let omitted_args_text = if let Some(args) = omitted_arguments {
                    format!(" (omitted arguments: {}", args)
                } else {
                    String::new()
                };
                format!(
                    "FFI function call{}: {}",
                    omitted_args_text,
                    cpp_function.short_text()
                )
            }
            CppFfiFunctionKind::FieldAccessor {
                ref field,
                ref accessor_type,
            } => format!("FFI field {:?}: {}", accessor_type, field.short_text()),
        }
    }
}

/// FFI function type with attached information about
/// corresponding original C++ type and their relation
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CppFfiType {
    /// Original C++ type
    pub original_type: CppType,
    /// FFI function type
    pub ffi_type: CppType,
    /// Relation
    pub conversion: CppTypeConversionToFfi,
}

impl CppFfiType {
    /// Generates an object representing the void type
    pub fn void() -> Self {
        CppFfiType {
            original_type: CppType::Void,
            ffi_type: CppType::Void,
            conversion: CppTypeConversionToFfi::NoChange,
        }
    }
}

/// Information about a Qt slot wrapper with
/// certain slot arguments
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QtSlotWrapper {
    /// Generated name of the wrapper class
    pub class_path: CppPath,
    /// Arguments of the slot.
    pub arguments: Vec<CppFfiType>,
    /// The function pointer type accepted by this wrapper
    pub function_type: CppFunctionPointerType,
    /// String identifier passed to `QObject::connect` function to
    /// specify the object's slot.
    pub receiver_id: String,
}