fix(trixy-types): Conform to the api provided by the headers
See the previous commit for an explanation
This commit is contained in:
parent
f699ca24a9
commit
86b946b540
|
@ -36,21 +36,21 @@ mod pure_header;
|
|||
mod structs_init;
|
||||
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";
|
||||
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:
|
||||
///
|
||||
/// *Trixy:*
|
||||
/// ```text
|
||||
/// fn fn_alone();
|
||||
/// nasp one {
|
||||
/// mod one {
|
||||
/// fn fn_one();
|
||||
/// nasp two {
|
||||
/// mod two {
|
||||
/// fn fn_two();
|
||||
/// nasp three {
|
||||
/// mod three {
|
||||
/// fn fn_three();
|
||||
/// }
|
||||
/// }
|
||||
|
@ -58,7 +58,7 @@ const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
|
|||
/// ```
|
||||
/// *C header:*
|
||||
/// ```text
|
||||
/// #ifndef TRIXY_MAIN_HEADER
|
||||
/// #if !defined TRIXY_MAIN_HEADER
|
||||
/// #define TRIXY_MAIN_HEADER
|
||||
///
|
||||
/// 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_three_fn_three();
|
||||
///
|
||||
/// typedef struct {
|
||||
/// struct three {
|
||||
/// void (*fn_three)(void);
|
||||
/// } three_t;
|
||||
/// typedef struct {
|
||||
/// };
|
||||
/// struct two {
|
||||
/// void (*fn_two)(void);
|
||||
/// three_t three;
|
||||
/// } two_t;
|
||||
/// typedef struct {
|
||||
/// };
|
||||
/// struct one {
|
||||
/// void (*fn_one)(void);
|
||||
/// two_t two;
|
||||
/// } one_t;
|
||||
/// } ;
|
||||
///
|
||||
/// const three_t three = {
|
||||
/// const struct three three = {
|
||||
/// .fn_three = one_two_three_fn_three,
|
||||
/// };
|
||||
/// const two_t two = {
|
||||
/// const struct two two = {
|
||||
/// .fn_two = one_two_fn_two,
|
||||
/// .three = three,
|
||||
/// };
|
||||
/// const one_t one = {
|
||||
/// const struct one one = {
|
||||
/// .fn_one = one_fn_one,
|
||||
/// .two = two,
|
||||
/// };
|
||||
/// #endif // ifndef TRIXY_MAIN_HEADER
|
||||
/// #endif // if !defined TRIXY_MAIN_HEADER
|
||||
/// ```
|
||||
pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String {
|
||||
let pure_header = pure_header::generate(&trixy);
|
||||
|
|
|
@ -54,10 +54,10 @@ fn structure_to_header(structure: &Structure) -> String {
|
|||
format!(
|
||||
"
|
||||
{}
|
||||
typedef struct {{
|
||||
struct {} {{
|
||||
{}
|
||||
}} {};",
|
||||
doc_comments, contents, ident
|
||||
}};",
|
||||
doc_comments, ident, contents
|
||||
)
|
||||
}
|
||||
fn doc_named_type_to_header(doc_named_type: &DocNamedType) -> String {
|
||||
|
@ -94,10 +94,10 @@ fn enumeration_to_header(enumeration: &Enumeration) -> String {
|
|||
format!(
|
||||
"
|
||||
{}
|
||||
typedef enum {{
|
||||
enum {} {{
|
||||
{}
|
||||
}} {};",
|
||||
doc_comments, states, ident
|
||||
}};",
|
||||
doc_comments, ident, states
|
||||
)
|
||||
}
|
||||
fn doc_identifier_to_header(doc_identifier: &DocIdentifier) -> String {
|
||||
|
|
|
@ -51,15 +51,15 @@ fn function_to_typedef(function: &Function) -> TokenStream2 {
|
|||
|
||||
fn namespace_to_typedef(namespace: &Namespace) -> TokenStream2 {
|
||||
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! {
|
||||
#type_ident #ident ;
|
||||
struct #type_ident #ident;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
.attributes
|
||||
.iter()
|
||||
|
@ -84,10 +84,10 @@ fn namespace_to_full_typedef(nasp: &Namespace) -> String {
|
|||
.join("\n");
|
||||
|
||||
let namespace = quote! {
|
||||
typedef struct {
|
||||
struct #ident {
|
||||
#functions
|
||||
#namespaces
|
||||
} #ident;
|
||||
};
|
||||
};
|
||||
format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::{
|
|||
config::TrixyConfig,
|
||||
generate::{
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -30,4 +30,3 @@ proc-macro2 = "1.0.70"
|
|||
quote = "1.0.33"
|
||||
syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] }
|
||||
thiserror = "1.0.51"
|
||||
trixy-types-derive = { path = "./trixy-types-derive" }
|
||||
|
|
|
@ -32,4 +32,10 @@ pub enum TypeConversionError {
|
|||
|
||||
#[error("You passed a untyped (type_id == Unknown) value to the conversion function!")]
|
||||
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 },
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
* 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 traits;
|
||||
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
|
||||
/// 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),
|
||||
("Result", 2),
|
||||
("Option", 1),
|
||||
("Vec", 1),
|
||||
("Result", 2),
|
||||
];
|
||||
|
||||
/// 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] = [
|
||||
header!("errno.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 const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 3] =
|
||||
[header!("errno.h"), header!("string.h"), header!("vec.h")];
|
||||
|
||||
pub fn header_names() -> std::string::String {
|
||||
C_TYPE_HEADER
|
||||
|
|
|
@ -1,108 +1,166 @@
|
|||
use crate::error::{self, TypeConversionError};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::{CStr, CString},
|
||||
os::raw::c_void,
|
||||
error::Error,
|
||||
ffi::{c_char, CString},
|
||||
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 {
|
||||
/// Turn the value into a c void pointer.
|
||||
/// It should try its best to return a value which c can understand.
|
||||
/// If this however is not possible, it should still return the underlying raw data
|
||||
fn into_void(self) -> *const c_void;
|
||||
type Ptr;
|
||||
/// Turn the value into a c void pointer or a data structure that c can understand.
|
||||
fn into_ptr(self) -> Self::Ptr;
|
||||
|
||||
/// 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 {
|
||||
fn into_void(self) -> *const c_void {
|
||||
self.0 as *const _ as *const c_void
|
||||
macro_rules! primitive_convert {
|
||||
[$name:ty] => {
|
||||
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 {
|
||||
fn into_void(self) -> *const c_void {
|
||||
self.0 as *const _ as *const c_void
|
||||
}
|
||||
}
|
||||
// Unsigned
|
||||
primitive_convert!(u8);
|
||||
primitive_convert!(u16);
|
||||
primitive_convert!(u32);
|
||||
primitive_convert!(u64);
|
||||
|
||||
impl Convertible for crate::Option {
|
||||
fn into_void(mut self) -> *const c_void {
|
||||
&mut self as *const _ as *const c_void
|
||||
}
|
||||
}
|
||||
// Signed
|
||||
primitive_convert!(i8);
|
||||
primitive_convert!(i16);
|
||||
primitive_convert!(i32);
|
||||
primitive_convert!(i64);
|
||||
|
||||
impl Convertible for crate::Vec {
|
||||
fn into_void(mut self) -> *const c_void {
|
||||
&mut self as *const _ as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
// Float
|
||||
primitive_convert!(f32);
|
||||
primitive_convert!(f64);
|
||||
|
||||
impl Convertible for CString {
|
||||
fn into_void(self) -> *const c_void {
|
||||
Into::<crate::String>::into(self).into_void()
|
||||
type Ptr = *const *const c_char;
|
||||
|
||||
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 () {
|
||||
fn into_void(self) -> *const c_void {
|
||||
std::ptr::null()
|
||||
impl<T: Convertible> Convertible for Option<T> {
|
||||
type Ptr = *const T;
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||
unreachable!(
|
||||
"
|
||||
This should never be called, as this option type resolves
|
||||
to a null pointer or to a pointer to the real value
|
||||
(e. g. a `crate::Vec<T>`)
|
||||
"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extern crate trixy_types_derive;
|
||||
pub use trixy_types_derive::{Convertible, TypeInfo};
|
||||
impl<T: Convertible, E: Error> Convertible for Result<T, E> {
|
||||
type Ptr = *const T;
|
||||
|
||||
/// Similar to [`std::any::type_name`] but only really works for *some* types.
|
||||
/// If the type is unknown it will just return [`TypeId::Unknown`]
|
||||
pub trait TypeInfo {
|
||||
fn type_of(&self) -> crate::TypeId;
|
||||
fn into_ptr(self) -> Self::Ptr {
|
||||
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>`)
|
||||
"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! type_info {
|
||||
($name:tt<_>, $output:ident) => {
|
||||
impl<T> TypeInfo for $name<T> {
|
||||
fn type_of(&self) -> crate::TypeId {
|
||||
crate::TypeId::$output
|
||||
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) => {
|
||||
impl<T, E> TypeInfo for $name<T, E> {
|
||||
fn type_of(&self) -> crate::TypeId {
|
||||
crate::TypeId::$output
|
||||
drop(vec);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
($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);
|
||||
|
|
|
@ -18,212 +18,5 @@
|
|||
* 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 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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,70 +1,12 @@
|
|||
// 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>
|
||||
|
||||
use std::ffi::{c_char, c_void};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct String(pub(crate) *const c_char);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types)]
|
||||
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 struct Vec<T> {
|
||||
/// You should cast this value to it's supposed type (readable from the Trixy api definition
|
||||
/// file)
|
||||
pub(crate) data: *const T,
|
||||
pub(crate) length: 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,
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
# build
|
||||
/target
|
||||
/result
|
||||
|
||||
# This crate is a library
|
||||
Cargo.lock
|
|
@ -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
|
|
@ -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()
|
||||
}
|
Reference in New Issue