fix(trixy-types): Conform to the api provided by the headers

See the previous commit for an explanation
This commit is contained in:
Benedikt Peetz 2024-02-18 13:35:40 +01:00
parent f699ca24a9
commit 86b946b540
Signed by: bpeetz
GPG Key ID: A5E94010C3A642AD
13 changed files with 202 additions and 457 deletions

View File

@ -36,21 +36,21 @@ mod pure_header;
mod structs_init; mod structs_init;
mod typedef; mod typedef;
const BEGIN_HEADER_GUARD: &'static str = r"#ifndef TRIXY_MAIN_HEADER const BEGIN_HEADER_GUARD: &'static str = r"#if !defined TRIXY_MAIN_HEADER
#define TRIXY_MAIN_HEADER"; #define TRIXY_MAIN_HEADER";
const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER"; const END_HEADER_GUARD: &'static str = r"#endif // if !defined TRIXY_MAIN_HEADER";
/// This function acts as the core transformative aspect, turning this trixy code into the /// This function acts as the core transformative aspect, turning this Trixy code into the
/// following c header: /// following c header:
/// ///
/// *Trixy:* /// *Trixy:*
/// ```text /// ```text
/// fn fn_alone(); /// fn fn_alone();
/// nasp one { /// mod one {
/// fn fn_one(); /// fn fn_one();
/// nasp two { /// mod two {
/// fn fn_two(); /// fn fn_two();
/// nasp three { /// mod three {
/// fn fn_three(); /// fn fn_three();
/// } /// }
/// } /// }
@ -58,7 +58,7 @@ const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
/// ``` /// ```
/// *C header:* /// *C header:*
/// ```text /// ```text
/// #ifndef TRIXY_MAIN_HEADER /// #if !defined TRIXY_MAIN_HEADER
/// #define TRIXY_MAIN_HEADER /// #define TRIXY_MAIN_HEADER
/// ///
/// extern int fn_alone(); /// extern int fn_alone();
@ -66,30 +66,30 @@ const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
/// extern int one_two_fn_two(); /// extern int one_two_fn_two();
/// extern int one_two_three_fn_three(); /// extern int one_two_three_fn_three();
/// ///
/// typedef struct { /// struct three {
/// void (*fn_three)(void); /// void (*fn_three)(void);
/// } three_t; /// };
/// typedef struct { /// struct two {
/// void (*fn_two)(void); /// void (*fn_two)(void);
/// three_t three; /// three_t three;
/// } two_t; /// };
/// typedef struct { /// struct one {
/// void (*fn_one)(void); /// void (*fn_one)(void);
/// two_t two; /// two_t two;
/// } one_t; /// } ;
/// ///
/// const three_t three = { /// const struct three three = {
/// .fn_three = one_two_three_fn_three, /// .fn_three = one_two_three_fn_three,
/// }; /// };
/// const two_t two = { /// const struct two two = {
/// .fn_two = one_two_fn_two, /// .fn_two = one_two_fn_two,
/// .three = three, /// .three = three,
/// }; /// };
/// const one_t one = { /// const struct one one = {
/// .fn_one = one_fn_one, /// .fn_one = one_fn_one,
/// .two = two, /// .two = two,
/// }; /// };
/// #endif // ifndef TRIXY_MAIN_HEADER /// #endif // if !defined TRIXY_MAIN_HEADER
/// ``` /// ```
pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String { pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String {
let pure_header = pure_header::generate(&trixy); let pure_header = pure_header::generate(&trixy);

View File

@ -54,10 +54,10 @@ fn structure_to_header(structure: &Structure) -> String {
format!( format!(
" "
{} {}
typedef struct {{ struct {} {{
{} {}
}} {};", }};",
doc_comments, contents, ident doc_comments, ident, contents
) )
} }
fn doc_named_type_to_header(doc_named_type: &DocNamedType) -> String { fn doc_named_type_to_header(doc_named_type: &DocNamedType) -> String {
@ -94,10 +94,10 @@ fn enumeration_to_header(enumeration: &Enumeration) -> String {
format!( format!(
" "
{} {}
typedef enum {{ enum {} {{
{} {}
}} {};", }};",
doc_comments, states, ident doc_comments, ident, states
) )
} }
fn doc_identifier_to_header(doc_identifier: &DocIdentifier) -> String { fn doc_identifier_to_header(doc_identifier: &DocIdentifier) -> String {

View File

@ -51,15 +51,15 @@ fn function_to_typedef(function: &Function) -> TokenStream2 {
fn namespace_to_typedef(namespace: &Namespace) -> TokenStream2 { fn namespace_to_typedef(namespace: &Namespace) -> TokenStream2 {
let ident = identifier_to_rust(&namespace.name); let ident = identifier_to_rust(&namespace.name);
let type_ident = format_ident!("{}_t", ident.to_string()); let type_ident = format_ident!("{}", ident.to_string());
quote! { quote! {
#type_ident #ident ; struct #type_ident #ident;
} }
} }
fn namespace_to_full_typedef(nasp: &Namespace) -> String { fn namespace_to_full_typedef(nasp: &Namespace) -> String {
let ident = format_ident!("{}_t", nasp.name.name); let ident = format_ident!("{}", nasp.name.name);
let doc_comments = nasp let doc_comments = nasp
.attributes .attributes
.iter() .iter()
@ -84,10 +84,10 @@ fn namespace_to_full_typedef(nasp: &Namespace) -> String {
.join("\n"); .join("\n");
let namespace = quote! { let namespace = quote! {
typedef struct { struct #ident {
#functions #functions
#namespaces #namespaces
} #ident; };
}; };
format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace} format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace}
} }

View File

@ -27,7 +27,7 @@ use crate::{
config::TrixyConfig, config::TrixyConfig,
generate::{ generate::{
c_api::{mangle_c_function_ident, namespaces_to_path, type_to_c_equalivalent}, c_api::{mangle_c_function_ident, namespaces_to_path, type_to_c_equalivalent},
function_identifier_to_rust, identifier_to_rust, named_type_to_rust, function_identifier_to_rust, identifier_to_rust,
}, },
}; };

View File

@ -30,4 +30,3 @@ proc-macro2 = "1.0.70"
quote = "1.0.33" quote = "1.0.33"
syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] } syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] }
thiserror = "1.0.51" thiserror = "1.0.51"
trixy-types-derive = { path = "./trixy-types-derive" }

View File

@ -32,4 +32,10 @@ pub enum TypeConversionError {
#[error("You passed a untyped (type_id == Unknown) value to the conversion function!")] #[error("You passed a untyped (type_id == Unknown) value to the conversion function!")]
UntypedInput, UntypedInput,
#[error("You passed an unaligned pointer to a function expecting a pointer aligned to it's required value!")]
NotAligned,
#[error("The returned Result was an error: {original_message}")]
ResultWasErr { original_message: String },
} }

View File

@ -18,7 +18,7 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
//! Trixy contains the types used by the [trixy-macros] crate to provide ffi safe types //! Trixy contains the types used by the [`trixy-macros`] crate to provide ffi safe types
pub mod error; pub mod error;
pub mod traits; pub mod traits;
pub mod types_list; pub mod types_list;
@ -33,28 +33,32 @@ macro_rules! header {
/// These are the "primitive" types used in Trixy, you can use any of them to create new structures /// These are the "primitive" types used in Trixy, you can use any of them to create new structures
/// The str is the name, the index represents the expected generic arguments /// The str is the name, the index represents the expected generic arguments
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 5] = [ ///
("str", 0), // NOTE: Every type here must have the [`Convertible`] implemented on it (@soispha)
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 14] = [
// Unsigned
("u8", 0),
("u16", 0),
("u32", 0),
("u64", 0),
// Signed
("i8", 0),
("i16", 0),
("i32", 0),
("i64", 0),
// Float
("f32", 0),
("f64", 0),
// Other
("String", 0), ("String", 0),
("Result", 2),
("Option", 1), ("Option", 1),
("Vec", 1), ("Vec", 1),
("Result", 2),
]; ];
/// The first value is the file name, the second it's contents /// The first value is the file name, the second it's contents
pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 9] = [ pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 3] =
header!("errno.h"), [header!("errno.h"), header!("string.h"), header!("vec.h")];
// These must be *before* "type_id.h"
header!("type_id_dec.h"),
header!("result_dec.h"),
header!("option.h"),
header!("string.h"),
header!("vec_dec.h"),
header!("type_id.h"),
header!("result.h"),
header!("vec.h"),
];
pub fn header_names() -> std::string::String { pub fn header_names() -> std::string::String {
C_TYPE_HEADER C_TYPE_HEADER

View File

@ -1,108 +1,166 @@
use crate::error::{self, TypeConversionError};
use std::{ use std::{
any::Any, error::Error,
ffi::{CStr, CString}, ffi::{c_char, CString},
os::raw::c_void, mem::ManuallyDrop,
ptr,
}; };
/// Convert a value use super::errno;
/// Convert a value into a data structure that we can send directly to c.
pub trait Convertible { pub trait Convertible {
/// Turn the value into a c void pointer. type Ptr;
/// It should try its best to return a value which c can understand. /// Turn the value into a c void pointer or a data structure that c can understand.
/// If this however is not possible, it should still return the underlying raw data fn into_ptr(self) -> Self::Ptr;
fn into_void(self) -> *const c_void;
/// If the data returned by [`into_ptr`] needs to be dropped this function should do it.
fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError>;
} }
impl Convertible for crate::String { macro_rules! primitive_convert {
fn into_void(self) -> *const c_void { [$name:ty] => {
self.0 as *const _ as *const c_void impl Convertible for $name {
type Ptr = $name;
fn into_ptr(self) -> Self::Ptr {
self
}
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
unreachable!("Theres is no need to drop a {}", stringify!($name))
}
}
} }
} }
impl Convertible for crate::str { // Unsigned
fn into_void(self) -> *const c_void { primitive_convert!(u8);
self.0 as *const _ as *const c_void primitive_convert!(u16);
} primitive_convert!(u32);
} primitive_convert!(u64);
impl Convertible for crate::Option { // Signed
fn into_void(mut self) -> *const c_void { primitive_convert!(i8);
&mut self as *const _ as *const c_void primitive_convert!(i16);
} primitive_convert!(i32);
} primitive_convert!(i64);
impl Convertible for crate::Vec { // Float
fn into_void(mut self) -> *const c_void { primitive_convert!(f32);
&mut self as *const _ as *const c_void primitive_convert!(f64);
}
}
impl Convertible for crate::Result {
fn into_void(mut self) -> *const c_void {
&mut self as *const _ as *const c_void
}
}
impl<T: Convertible + Any + TypeInfo, E: Convertible + Any + TypeInfo> Convertible
for Result<T, E>
{
fn into_void(self) -> *const c_void {
Into::<crate::Result>::into(self).into_void()
}
}
impl Convertible for &CStr {
fn into_void(self) -> *const c_void {
Into::<crate::str>::into(self).into_void()
}
}
impl Convertible for CString { impl Convertible for CString {
fn into_void(self) -> *const c_void { type Ptr = *const *const c_char;
Into::<crate::String>::into(self).into_void()
fn into_ptr(self) -> Self::Ptr {
self.into_raw() as *const *const c_char
}
fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
unsafe {
if ptr.is_null() {
return Err(TypeConversionError::NullPointer);
}
// TODO(@soispha): Add this, when this feature is stable <2024-02-17>
// if !ptr.is_aligned() {
// return Err(TypeConversionError::NotAligned);
// }
// SAFETY:
// Checked that the ptr is not null and that it is aligned (to c_char).
// This should hopefully be enough to avoid constructing a CString from a pointer not
// constructed by a us earlier in the [`into_ptr`] function.
// However:
// This still builds on the hope that c passes the pointer with the length unchanged.
let string = CString::from_raw(ptr as *mut i8);
drop(string);
}
Ok(())
} }
} }
impl Convertible for () { impl<T: Convertible> Convertible for Option<T> {
fn into_void(self) -> *const c_void { type Ptr = *const T;
std::ptr::null()
fn into_ptr(self) -> Self::Ptr {
if let Some(inner) = self {
let mut_ref = &mut inner.into_ptr();
mut_ref as *mut _ as *const T
} else {
ptr::null()
} }
} }
extern crate trixy_types_derive; fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
pub use trixy_types_derive::{Convertible, TypeInfo}; unreachable!(
"
/// Similar to [`std::any::type_name`] but only really works for *some* types. This should never be called, as this option type resolves
/// If the type is unknown it will just return [`TypeId::Unknown`] to a null pointer or to a pointer to the real value
pub trait TypeInfo { (e. g. a `crate::Vec<T>`)
fn type_of(&self) -> crate::TypeId; "
);
}
} }
macro_rules! type_info { impl<T: Convertible, E: Error> Convertible for Result<T, E> {
($name:tt<_>, $output:ident) => { type Ptr = *const T;
impl<T> TypeInfo for $name<T> {
fn type_of(&self) -> crate::TypeId { fn into_ptr(self) -> Self::Ptr {
crate::TypeId::$output match self {
Ok(ok) => {
let mut_ref = &mut ok.into_ptr();
mut_ref as *mut _ as *const T
}
Err(err) => {
errno::set(TypeConversionError::ResultWasErr {
original_message: err.to_string(),
});
ptr::null()
} }
} }
}
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
unreachable!(
"
This should never be called, as this result type resolves
to a null pointer (and a set error) or to a pointer to
the real value (e. g. a `crate::Vec<T>`)
"
);
}
}
impl<T> Convertible for Vec<T> {
type Ptr = crate::Vec<T>;
fn into_ptr(self) -> Self::Ptr {
let mut me = ManuallyDrop::new(self);
Self::Ptr {
data: me.as_mut_ptr() as *const T,
length: me.len(),
capacity: me.capacity(),
}
}
fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
let vec = unsafe {
if ptr.data.is_null() {
return Err(TypeConversionError::NullPointer);
}
// TODO(@soispha): Add this, when this feature is stable <2024-02-17>
// if !ptr.data.is_aligned() {
// return Err(TypeConversionError::NotAligned);
// }
// SAFETY:
// We simply hope that c treated our vector as read-only and didn't modify it.
// See the SAFETY section of the String implementation for more detail.
Vec::from_raw_parts(ptr.data as *mut T, ptr.length, ptr.capacity)
}; };
($name:tt<_,_>, $output:ident) => { drop(vec);
impl<T, E> TypeInfo for $name<T, E> { Ok(())
fn type_of(&self) -> crate::TypeId {
crate::TypeId::$output
} }
} }
};
($name:ty, $output:ident) => {
impl TypeInfo for $name {
fn type_of(&self) -> crate::TypeId {
crate::TypeId::$output
}
}
};
}
type_info!((), void);
type_info!(&CStr, str_t);
type_info!(CString, string_t);
type_info!(Result<_, _>, result_t);
type_info!(Vec<_>, vec_t);
type_info!(Option<_>, option_t);

View File

@ -18,212 +18,5 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::{
alloc::Layout,
ffi::{c_char, CStr, CString},
mem::ManuallyDrop,
ptr,
};
use crate::error::TypeConversionError;
use self::convert_trait::{Convertible, TypeInfo};
pub mod convert_trait; pub mod convert_trait;
pub mod errno; pub mod errno;
impl<'a> TryFrom<&'a crate::str> for &'a str {
type Error = crate::error::TypeConversionError;
fn try_from(value: &'a crate::str) -> Result<Self, Self::Error> {
let ptr = value.0;
if ptr.is_null() {
Err(TypeConversionError::NullPointer)
} else {
// SAFETY: We checked for null, which is a huge upside other things that we simply hope
// for:
// - null terminated
// - actually be a valid pointer (`(void*) 2` is *valid* c but not a *valid* pointer)
// - be contained in a single allocated object
// - the memory must obviously not be mutated, while this &str exists
// - the null terminator must be within `isize::MAX` from the start position
let str = unsafe { CStr::from_ptr(*ptr as *const c_char) };
str.to_str()
.map_err(|_err| crate::error::TypeConversionError::String {
got: value.0 as *const c_char,
})
}
}
}
impl TryFrom<crate::String> for String {
type Error = crate::error::TypeConversionError;
fn try_from(value: crate::String) -> Result<Self, Self::Error> {
let ptr = value.0;
if ptr.is_null() {
Err(TypeConversionError::NullPointer)
} else {
// SAFETY: (exactly the same as above applies)
let string = unsafe { CStr::from_ptr(ptr) }.to_owned();
Ok(string
.into_string()
.map_err(|err| TypeConversionError::String {
got: err.into_cstring().into_raw(),
})?)
}
}
}
impl<T: Convertible + TypeInfo> TryFrom<crate::Vec> for Vec<T> {
type Error = crate::error::TypeConversionError;
fn try_from(value: crate::Vec) -> Result<Self, Self::Error> {
match value.type_id {
crate::TypeId::Unknown => Err(Self::Error::UntypedInput),
crate::TypeId::void => Ok(vec![]),
crate::TypeId::str_t => {
let mut output: Vec<T> = Vec::with_capacity(value.capacity);
for i in 0..value.size {
// output.push(value.data.add())
}
Ok(output)
},
crate::TypeId::string_t => todo!(),
crate::TypeId::result_t => todo!(),
crate::TypeId::vec_t => todo!(),
crate::TypeId::option_t => todo!(),
}
}
}
impl crate::Vec {
pub fn pop<T: Convertible + TypeInfo>(&mut self) -> Result<T, TypeConversionError> {
match self.type_id {
crate::TypeId::Unknown => Err(TypeConversionError::UntypedInput),
crate::TypeId::void => Ok(() as T),
crate::TypeId::str_t => todo!(),
crate::TypeId::string_t => todo!(),
crate::TypeId::result_t => todo!(),
crate::TypeId::vec_t => todo!(),
crate::TypeId::option_t => todo!(),
}
}
}
impl From<CString> for crate::String {
fn from(value: CString) -> Self {
let layout = Layout::array::<u8>(value.as_bytes_with_nul().len()).expect(
"We don't handle n > isize::MAX, in the other types. \
Thus, this convertion will fail",
);
// SAFETY:
// - The layout should never have zero size (at least 1)
// - The memory blocks are copied from the string, thus initialization is irrelevant
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() {
// TODO(@soispha): How do we handle this? <2023-12-27>
panic!("While preparing a string for c failed to allocate memory, the pointer is null");
}
// SAFETY:
// - Both are valid for reads of `count * size_of::<char>()` bytes because we checked
// their length
// - They are properly aligned for types of [`c_char`]
unsafe {
ptr::copy(
value.as_ptr(),
ptr as *mut i8,
value.as_bytes_with_nul().len(),
)
};
Self(ptr as *const c_char)
}
}
impl<'a> From<&'a CStr> for crate::str {
fn from(value: &'a CStr) -> Self {
crate::str(value.as_ptr() as *const u8)
}
}
impl<T> From<Vec<T>> for crate::Vec
where
T: Convertible + TypeInfo,
{
fn from(value: Vec<T>) -> Self {
if value.is_empty() {
Self {
type_id: crate::TypeId::Unknown,
data: ptr::null(),
size: 0,
capacity: 0,
}
} else {
let value_type = value.first().expect("We checked the length").type_of();
let value: Vec<_> = value.into_iter().map(|val| val.into_void()).collect();
// We simply tell rust, that c should drop the value
let vec = ManuallyDrop::new(value);
Self {
type_id: value_type,
data: *vec.first().expect("This exists"),
size: vec.len(),
capacity: vec.capacity(),
}
}
}
}
impl<T> From<Option<T>> for crate::Option
where
T: Convertible + TypeInfo,
{
fn from(value: Option<T>) -> Self {
match value {
Some(value) => Self {
type_id: value.type_of(),
value: value.into_void(),
some: true,
},
None => Self {
type_id: crate::TypeId::Unknown,
value: ptr::null(),
some: false,
},
}
}
}
// impl<'a> From<&'a str> for crate::str {
// fn from(value: &'a str) -> Self {
// let ptr = value.as_ptr();
// // This is taken from std's CStr
//
// // SAFETY: We do not handle any strings larger than `isize::MAX` thus this call works.
// //
// // The cast from c_char to i8 is ok because a c_char is always one byte.
// unsafe { CStr::from_ptr(ptr as *const i8) }.into()
// }
// }
impl<T, E> From<Result<T, E>> for crate::Result
where
T: Convertible + TypeInfo,
E: Convertible + TypeInfo,
{
fn from(value: Result<T, E>) -> Self {
match value {
Ok(ok) => Self {
type_id: ok.type_of(),
tag: crate::ResultTag::Ok,
value: ok.into_void(),
},
Err(err) => Self {
type_id: err.type_of(),
tag: crate::ResultTag::Err,
value: err.into_void(),
},
}
}
}

View File

@ -1,70 +1,12 @@
// NOTE(@soispha): All types specified here *MUST* be include in the BASE_TYPES constant, otherwise // NOTE(@soispha): All types specified here *MUST* be include in the BASE_TYPES constant, otherwise
// they are not usable from Trixy code <2023-12-25> // they are not usable from Trixy code <2023-12-25>
use std::ffi::{c_char, c_void};
#[derive(Debug)] #[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct String(pub(crate) *const c_char); pub struct Vec<T> {
/// You should cast this value to it's supposed type (readable from the Trixy api definition
#[derive(Debug)] /// file)
#[repr(C)] pub(crate) data: *const T,
#[allow(non_camel_case_types)] pub(crate) length: usize,
pub struct str(pub(crate) *const u8);
#[derive(Debug)]
#[repr(C)]
pub enum TypeId {
/// We simply don't know which type this is
Unknown,
#[allow(non_camel_case_types)]
void,
#[allow(non_camel_case_types)]
str_t,
#[allow(non_camel_case_types)]
string_t,
#[allow(non_camel_case_types)]
result_t,
#[allow(non_camel_case_types)]
vec_t,
#[allow(non_camel_case_types)]
option_t,
}
#[derive(Debug)]
#[repr(C)]
pub enum ResultTag {
Ok,
Err,
}
#[derive(Debug)]
#[repr(C)]
pub struct Result {
pub(crate) type_id: TypeId,
pub(crate) tag: ResultTag,
pub(crate) value: *const c_void,
}
#[derive(Debug)]
#[repr(C)]
pub struct Vec {
pub(crate) type_id: TypeId,
pub(crate) data: *const c_void,
pub(crate) size: usize,
pub(crate) capacity: usize, pub(crate) capacity: usize,
} }
#[derive(Debug)]
#[repr(C)]
pub struct Option {
pub(crate) type_id: TypeId,
pub(crate) value: *const c_void,
pub(crate) some: bool,
}

View File

@ -1,6 +0,0 @@
# build
/target
/result
# This crate is a library
Cargo.lock

View File

@ -1,13 +0,0 @@
[package]
name = "trixy-types-derive"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
syn = "1.0"
quote = "1.0"
[lib]
proc-macro = true

View File

@ -1,38 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
#[proc_macro_derive(Convertible)]
pub fn convertible_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast: syn::DeriveInput = syn::parse(input).unwrap();
// Build the trait implementation
let name = &ast.ident;
let gen = quote! {
impl trixy::types::traits::convert_trait::Convertible for #name {
fn into_void(mut self) -> *const core::ffi::c_void {
&mut self as *const _ as *const core::ffi::c_void
}
}
};
gen.into()
}
#[proc_macro_derive(TypeInfo)]
pub fn type_info_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast: syn::DeriveInput = syn::parse(input).unwrap();
// Build the trait implementation
let name = &ast.ident;
let gen = quote! {
impl trixy::types::traits::convert_trait::TypeInfo for #name {
fn type_of(&self) -> trixy::types::TypeId {
trixy::types::TypeId::Unknown
}
}
};
gen.into()
}