co3 0.4.0

Build C API from Rust
Documentation
use co3::{Tag, ReprC, ffi, rust_spec::RustSpec};

trait Attr {}

#[derive(Clone, RustSpec, ReprC)]
#[repr(transparent)]
struct Value(u8);

trait ByteValue {
    fn into_byte(self) -> u8;
}

#[derive(Clone, RustSpec, ReprC)]
#[repr(transparent)]
struct EnvAttr(usize);

#[derive(Clone, RustSpec, Tag, ReprC)]
#[tag(u16, unsafe(1))]
struct Custom(usize);

impl Attr for Custom {}

fn value_plain(value: Value) -> u8 {
    value.0
}

fn value_soft(value: &(u8,)) -> u8 {
    value.0
}

ffi! {
    #![unsafe(export("C"))]

    #![cfg(all())]
    #![cfg_attr(all(), cfg(all()))]
    #![cfg_attr(any(), symbol_prefix = "unused")]
    #![cfg_attr(all(), symbol_prefix = "cfg_attr")]

    #[cfg(all())]
    #[cfg_attr(any(), symbol_name = "unused")]
    #[cfg_attr(all(), symbol_name = "cfg_attr__value_plain")]
    fn value_plain(#[cfg(all())] value: Value) -> u8;
}

ffi! {
    #![unsafe(extern("C"))]

    #![cfg_attr(any(), symbol_prefix = "unused")]
    #![cfg_attr(all(), symbol_prefix = "cfg_attr")]

    #[cfg_attr(any(), symbol_name = "unused")]
    #[cfg_attr(all(), symbol_name = "cfg_attr__value_plain")]
    fn imported_value_plain(value: Value) -> u8;
}

ffi! {
    #![unsafe(extern("C"))]

    #[cfg(any())]
    fn disabled_static<T>()
    where
        use<T> @ <u8>;
}

ffi! {
    #![cfg_attr(all(), unsafe(export("C")))]

    #[symbol_name = "cfg_attr__value_soft"]
    fn value_soft(#[cfg_attr(all(), soft)] value: &(u8,)) -> u8;
}

ffi! {
    #![cfg_attr(all(), unsafe(extern("C")))]

    #[symbol_name = "cfg_attr__value_soft"]
    fn imported_value_soft(#[cfg_attr(all(), soft)] value: &(u8,)) -> u8;
}

struct ExportOpaque(u8);

ffi! {
    #![unsafe(export("C"))]

    #![symbol_prefix = "cfg_attr"]

    #[cfg(all())]
    #[cfg_attr(all(), tag(u8))]
    type ExportOpaque;
}

mod imported {
    use super::*;

    ffi! {
        #![unsafe(extern("C"))]

        #![symbol_prefix = "cfg_attr"]

        #[cfg_attr(all(), tag(u8))]
        type Opaque;
    }
}

mod provider {
    use super::*;

    #[derive(Clone, RustSpec, ReprC, Tag)]
    #[tag(u16, unsafe(1))]
    pub(super) struct Custom(usize);

    impl Attr for Custom {}

    impl ByteValue for Custom {
        fn into_byte(self) -> u8 {
            self.0 as u8
        }
    }

    ffi! {
        #![unsafe(export("C"))]

        #![symbol_prefix = "cfg_attr_dispatch"]

        #[cfg(all())]
        impl<dyn(u16) T: Attr = EnvAttr> ByteValue for T
        where
            use<T> @ <Custom>,
        {
            #[cfg(all())]
            fn into_byte(self) -> u8;
        }
    }
}

ffi! {
    #![unsafe(extern("C"))]

    #![symbol_prefix = "cfg_attr_dispatch"]

    #[cfg(all())]
    impl<dyn(u16) T: Attr = EnvAttr> ByteValue for T
    where
        use<T> @ <Custom>,
    {
        #[cfg(all())]
        fn into_byte(tag_id: <dyn T>::TAG, self) -> u8;
    }
}

fn main() {
    assert_eq!(imported_value_plain(Value(3)), 3);
    assert_eq!(imported_value_soft(&(4,)), 4);
    assert_eq!(Custom(5).into_byte(), 5);
}