interoptopus 0.16.0

The polyglot bindings generator for your library (C#, C, Python, ...). 🐙
Documentation
//! Type definitions for the FFI data model.
//!
//! Every Rust type that crosses the FFI boundary is described by a [`Type`] carrying
//! a [`TypeKind`] discriminant. The kind determines the structure: [`Primitive`],
//! [`Struct`], [`Enum`], [`Array`], function pointer, pointer, or a higher-level
//! [`TypePattern`] (options, slices, strings, …).

mod array;
mod enums;
mod fnptr;
mod pattern;
mod primitive;
mod std;
mod structs;
mod wire;

use crate::lang::function::Signature;
use crate::lang::meta::{Docs, Emission, Visibility};

use crate::inventory::{Inventory, TypeId};

pub use array::Array;
pub use enums::{Enum, Variant, VariantKind};
pub use pattern::TypePattern;
pub use primitive::{Primitive, PrimitiveValue};
pub use std::{type_id_ptr, type_id_ptr_mut};
pub use structs::{Field, Struct};
pub use wire::{WireIO, WireOnly};

pub trait TypeProxy {}

/// Implemented by every Rust type that can appear in an FFI signature.
///
/// The `#[ffi]` attribute generates this for annotated structs and enums.
/// Primitive types and built-in patterns have hand-written implementations.
///
/// # Safety
///
/// The metadata returned by this trait drives FFI code generation and
/// runtime type dispatch. An incorrect implementation — wrong `id()`,
/// mismatched `kind()`, or inaccurate safety flags — will cause the
/// generated bindings to misinterpret memory layouts, leading to
/// undefined behaviour during FFI calls.
pub unsafe trait TypeInfo {
    /// Whether this type can be used inside a [`Wire<T>`](crate::lang::types::WireOnly).
    const WIRE_SAFE: bool;
    /// Whether this type can be passed directly over the FFI boundary.
    const RAW_SAFE: bool;
    /// Whether this type can appear in an async service method.
    const ASYNC_SAFE: bool;
    /// Whether this type can appear in a service method.
    const SERVICE_SAFE: bool;
    /// Whether this type is valid as a service constructor return type.
    const SERVICE_CTOR_SAFE: bool;
    /// Whether `std::Option<Self>` uses a niche and is FFI-safe as a pointer
    /// (e.g., references, `NonNull`, function pointers).
    const OPTION_PTR_SAFE: bool = false;

    /// The unique identifier for this type.
    fn id() -> TypeId;
    /// The structural kind of this type.
    fn kind() -> TypeKind;
    /// The full type description.
    fn ty() -> Type;

    /// Registers this type and all its transitive dependencies with the inventory.
    fn register(inventory: &mut impl Inventory);
}

/// The structural classification of an FFI type.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TypeKind {
    /// A fixed-size array `[T; N]`.
    Array(Array),
    /// A primitive type (`u8`, `f32`, `bool`, …).
    Primitive(Primitive),
    /// A `#[repr(C)]` struct.
    Struct(Struct),
    /// A `#[repr(u32)]` (or similar) enum.
    Enum(Enum),
    /// A function pointer (`extern "C" fn(…) -> …`).
    FnPointer(Signature),
    /// A `*const T` pointer.
    ReadPointer(TypeId),
    /// A service (opaque, class-like). These can never be observed directly but only as
    /// a `&Service` or similar. If they do appear in a signature directly it's because some
    /// proc macro or codegen silently converts them to `&Service` behind the scenes.
    Service,
    /// A `*mut T` pointer.
    ReadWritePointer(TypeId),
    /// A type that may only be observed behind a pointer.
    Opaque,
    /// A type that can only appear inside a `Wire<T>`.
    WireOnly(WireOnly),
    /// A higher-level pattern type (option, slice, string, vec, …).
    TypePattern(TypePattern),
}

/// A named FFI type with its kind, documentation, and placement metadata.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Type {
    /// The type name used in generated bindings.
    pub name: String,
    /// Whether the type is public or private.
    pub visibility: Visibility,
    /// Documentation extracted from `///` comments.
    pub docs: Docs,
    /// Where the type definition should be placed.
    pub emission: Emission,
    /// The structural kind.
    pub kind: TypeKind,
}

/// How a struct or enum is laid out in memory.
#[derive(Clone, Copy, Debug, PartialOrd, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Layout {
    /// `#[repr(C)]` layout.
    C,
    /// `#[repr(transparent)]` layout.
    Transparent,
    /// `#[repr(C, packed)]` layout.
    Packed,
    /// Opaque (layout not exposed).
    Opaque,
    /// For use with enum discriminant (e.g., `#[repr(u32)]`).
    Primitive(Primitive),
}

/// The memory representation of a type: layout strategy plus optional alignment.
#[derive(Clone, Copy, Debug, PartialOrd, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Repr {
    /// The layout strategy.
    pub layout: Layout,
    /// An explicit alignment override, if any.
    pub alignment: Option<usize>,
}

impl Repr {
    #[must_use]
    pub fn c() -> Self {
        Self { layout: Layout::C, alignment: None }
    }

    #[must_use]
    pub fn u32() -> Self {
        Self { layout: Layout::Primitive(Primitive::U32), alignment: None }
    }
}

/// Compile-time assertion that `T` is wire-safe.
#[track_caller]
pub const fn assert_wire_safe<T: TypeInfo>() {
    assert!(T::WIRE_SAFE);
}

/// Compile-time assertion that `T` can be passed directly over the FFI boundary.
#[track_caller]
pub const fn assert_raw_safe<T: TypeInfo>() {
    assert!(T::RAW_SAFE, "This type cannot be safely passed over FFI boundaries.");
}

/// Compile-time assertion that `T` can appear in an async service method.
#[track_caller]
pub const fn assert_async_safe<T: TypeInfo>() {
    assert!(T::ASYNC_SAFE, "This type cannot be used in async functions or methods.");
}

/// Compile-time assertion that `T` can appear in a service method.
#[track_caller]
pub const fn assert_service_safe<T: TypeInfo>() {
    assert!(T::SERVICE_SAFE, "This type cannot be used in service methods.");
}

/// Compile-time assertion that `T` is `Send + Sync`.
pub const fn assert_send_sync<T: Send + Sync>() {}

/// Compile-time assertion that `T` is declared as a service type.
#[track_caller]
pub const fn assert_service_type<T: TypeInfo>() {
    assert!(T::SERVICE_SAFE, "This type is not declared as a service type. Use #[ffi(service)] on the struct definition to use it with an #[ffi] impl block.");
}

/// Compile-time assertion that `T` is a valid service constructor return type.
#[track_caller]
pub const fn assert_service_ctor_safe<T: TypeInfo>() {
    assert!(T::SERVICE_CTOR_SAFE, "This method looks like a constructor, but does not return ffi::Result<Self, _>");
}