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 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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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" }
|
|
||||||
|
|
|
@ -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 },
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
impl<T: Convertible, E: Error> Convertible for Result<T, E> {
|
||||||
pub use trixy_types_derive::{Convertible, TypeInfo};
|
type Ptr = *const T;
|
||||||
|
|
||||||
/// Similar to [`std::any::type_name`] but only really works for *some* types.
|
fn into_ptr(self) -> Self::Ptr {
|
||||||
/// If the type is unknown it will just return [`TypeId::Unknown`]
|
match self {
|
||||||
pub trait TypeInfo {
|
Ok(ok) => {
|
||||||
fn type_of(&self) -> crate::TypeId;
|
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 {
|
impl<T> Convertible for Vec<T> {
|
||||||
($name:tt<_>, $output:ident) => {
|
type Ptr = crate::Vec<T>;
|
||||||
impl<T> TypeInfo for $name<T> {
|
|
||||||
fn type_of(&self) -> crate::TypeId {
|
fn into_ptr(self) -> Self::Ptr {
|
||||||
crate::TypeId::$output
|
let mut me = ManuallyDrop::new(self);
|
||||||
}
|
Self::Ptr {
|
||||||
|
data: me.as_mut_ptr() as *const T,
|
||||||
|
length: me.len(),
|
||||||
|
capacity: me.capacity(),
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
($name:tt<_,_>, $output:ident) => {
|
|
||||||
impl<T, E> TypeInfo for $name<T, E> {
|
fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||||
fn type_of(&self) -> crate::TypeId {
|
let vec = unsafe {
|
||||||
crate::TypeId::$output
|
if ptr.data.is_null() {
|
||||||
|
return Err(TypeConversionError::NullPointer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
// TODO(@soispha): Add this, when this feature is stable <2024-02-17>
|
||||||
($name:ty, $output:ident) => {
|
// if !ptr.data.is_aligned() {
|
||||||
impl TypeInfo for $name {
|
// return Err(TypeConversionError::NotAligned);
|
||||||
fn type_of(&self) -> crate::TypeId {
|
// }
|
||||||
crate::TypeId::$output
|
|
||||||
}
|
// 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)
|
||||||
|
};
|
||||||
|
drop(vec);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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/>.
|
* 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(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
|
|
|
@ -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