feat(src): Add real and working support for results

Every `Result` is now -- on demand -- instantiated (i.e. the generic types
are replaced with the concretely specified ones). And can thus simply be
exported to c.
This commit is contained in:
Benedikt Peetz 2024-05-20 15:19:11 +02:00
parent 5d3afa3c5a
commit a33aa053d2
Signed by: bpeetz
GPG Key ID: B6139BCB07CE946D
15 changed files with 436 additions and 170 deletions

View File

@ -25,7 +25,7 @@ use crate::parser::command_spec::{Attribute, DocIdentifier, Enumeration};
impl Enumeration { impl Enumeration {
pub fn to_auxiliary_c(&self) -> String { pub fn to_auxiliary_c(&self) -> String {
let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes); let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes);
let ident = &self.identifier.to_auxiliary_c(); let ident = &self.identifier.to_auxiliary_c(&[]);
let states = self let states = self
.states .states
.iter() .iter()

View File

@ -24,15 +24,12 @@ use convert_case::{Case, Casing};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use crate::parser::command_spec::{Identifier, Variant}; use crate::parser::command_spec::{Identifier, Type, Variant};
mod doc_identifier; mod doc_identifier;
//
// pub use doc_identifier::*;
impl Identifier { impl Identifier {
pub fn to_auxiliary_c(&self) -> TokenStream2 { pub fn to_auxiliary_c(&self, generic_args: &[Type]) -> TokenStream2 {
let ident = self.to_rust_pascalized(); let ident = self.to_rust_pascalized();
match &self.variant { match &self.variant {
Variant::Structure { .. } => { Variant::Structure { .. } => {
@ -46,6 +43,13 @@ impl Identifier {
enum #ident enum #ident
} }
} }
Variant::Result { .. } => {
let ident = Type::mangle_result_name(generic_args);
quote! {
struct #ident
}
}
Variant::Primitive => match self.name.to_case(Case::Snake).as_str() { Variant::Primitive => match self.name.to_case(Case::Snake).as_str() {
"string" => { "string" => {
quote! { quote! {
@ -65,14 +69,15 @@ impl Identifier {
// Float // Float
"f_32" => quote! { float }, "f_32" => quote! { float },
"f_64" => quote! { double }, "f_64" => quote! { double },
// Other (not yet imlemented)
// Others (not yet imlemented)
// ("Option", 1), // ("Option", 1),
// ("Vec", 1), // ("Vec", 1),
// ("Result", 2),
other => { other => {
todo!("'{}' is not yet supported", other) todo!("'{}' is not yet supported", other)
} }
}, },
other => { other => {
unimplemented!("{:#?}", other) unimplemented!("{:#?}", other)
} }

View File

@ -24,7 +24,7 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use crate::parser::command_spec::{ use crate::parser::command_spec::{
Attribute, Enumeration, Function, Identifier, Namespace, Structure, Attribute, Enumeration, Function, Identifier, Namespace, Structure, Type,
}; };
impl Namespace { impl Namespace {
@ -44,6 +44,12 @@ impl Namespace {
.map(Enumeration::to_auxiliary_c) .map(Enumeration::to_auxiliary_c)
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"); .join("\n");
let results: String = self
.select_types_pred(|(ident, _generics)| ident.name.as_str() == "Result")
.iter()
.map(|(_, generics)| Type::to_auxilary_c_result(generics).to_string())
.collect::<Vec<String>>()
.join("\n");
let functions: String = self let functions: String = self
.functions .functions
.iter() .iter()
@ -56,7 +62,7 @@ impl Namespace {
.map(|nasp| nasp.to_auxiliary_c(&nasps)) .map(|nasp| nasp.to_auxiliary_c(&nasps))
.collect(); .collect();
format! {"{}\n{}\n{}\n{}", enumerations, structures, functions, namespaces} format! {"{}\n{}\n{}\n{}\n{}", enumerations, structures, results, functions, namespaces}
} }
pub fn to_auxiliary_c_full_struct_init(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 { pub fn to_auxiliary_c_full_struct_init(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 {

View File

@ -25,7 +25,7 @@ use crate::parser::command_spec::{Attribute, DocNamedType, Structure};
impl Structure { impl Structure {
pub fn to_auxiliary_c(&self) -> String { pub fn to_auxiliary_c(&self) -> String {
let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes); let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes);
let ident = self.identifier.to_auxiliary_c(); let ident = self.identifier.to_auxiliary_c(&[]);
let contents = self let contents = self
.contents .contents
.iter() .iter()

View File

@ -36,9 +36,9 @@ impl Type {
match self { match self {
Type::Typical { Type::Typical {
identifier, identifier,
generic_args: _, generic_args,
} => { } => {
let ident = identifier.to_auxiliary_c(); let ident = identifier.to_auxiliary_c(generic_args);
let output = if is_output { let output = if is_output {
quote! { quote! {
* *
@ -78,4 +78,21 @@ impl Type {
} }
} }
} }
pub fn to_auxilary_c_result(generic_args: &[Type]) -> TokenStream2 {
let ident = Type::mangle_result_name(&generic_args);
let generic_one = generic_args[0].to_auxiliary_c(false, None);
let generic_two = generic_args[1].to_auxiliary_c(false, None);
quote! {
struct #ident {
enum ResultTag tag;
union {
#generic_one ok;
#generic_two err;
} value;
};
}
}
} }

View File

@ -20,12 +20,14 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::iter;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use crate::{ use crate::{
macros::config::trixy::TrixyConfig, macros::config::trixy::TrixyConfig,
parser::command_spec::{Enumeration, Identifier, Namespace, Structure}, parser::command_spec::{Enumeration, Identifier, Namespace, Structure, Type},
}; };
impl Namespace { impl Namespace {
@ -48,11 +50,20 @@ impl Namespace {
let enumerations: TokenStream2 = self.enumerations.iter().map(Enumeration::to_c).collect(); let enumerations: TokenStream2 = self.enumerations.iter().map(Enumeration::to_c).collect();
let callback_function = format_ident!("{}", config.callback_function); let callback_function = format_ident!("{}", config.callback_function);
let all_results_types: TokenStream2 = self
.select_types_pred(|(identifier, _generic_args)| identifier.name.as_str() == "Result")
.iter()
.map(|(_, generics)| Type::to_c_result(&generics))
.collect();
quote! { quote! {
pub mod #ident { pub mod #ident {
#[allow(unused_imports)] #[allow(unused_imports)]
use crate :: #callback_function; use crate :: #callback_function;
#all_results_types
#enumerations #enumerations
#structures #structures
#additional_functions #additional_functions
@ -60,4 +71,81 @@ impl Namespace {
#functions #functions
} }
} }
/// Get all the types used in this namespaces that conform to the predicate.
/// The predicate get's the identifier of a type and the generic arguments passed.
pub fn select_types_pred(
&self,
pred: fn(&(Identifier, Vec<Type>)) -> bool,
) -> Vec<(Identifier, Vec<Type>)> {
fn filter_type(ty: &Type) -> Vec<(Identifier, Vec<Type>)> {
match ty {
Type::Typical {
identifier,
generic_args,
} => {
let mut output = vec![];
output.extend(generic_args.iter().map(|ga| filter_type(ga)).flatten());
output.push((identifier.to_owned(), generic_args.to_owned()));
// Stop recursion and return
output
}
Type::Function { inputs, output } => {
let mut full_output = vec![];
full_output.extend(
inputs
.iter()
.map(|na| &na.r#type)
.map(|ty| filter_type(&ty))
.flatten(),
);
if let Some(out) = output {
full_output.extend(filter_type(out))
}
full_output
}
}
}
let all_types = self.collect_types();
let all_result_types: Vec<_> = all_types
.iter()
.map(|r#type| filter_type(r#type))
.flatten()
.filter(pred)
.collect();
all_result_types
}
/// Collect a vector of all types used in this namespace
pub fn collect_types(&self) -> Vec<Type> {
let structures_types = self
.structures
.iter()
.map(|st| st.contents.iter().map(|dc| dc.r#type.clone()))
.flatten();
self.functions
.iter()
.map(|r#fn| {
r#fn.inputs
.iter()
.map(|nt| Some(nt.r#type.clone()))
.chain({
if let Some(output) = &r#fn.output {
iter::once(Some(output.clone()))
} else {
iter::once(None)
}
})
.filter_map(|x| x)
})
.flatten()
.chain(structures_types)
.collect()
}
} }

View File

@ -21,7 +21,8 @@
*/ */
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::{format_ident, quote};
use syn::Ident;
use crate::parser::command_spec::{Identifier, NamedType, Type}; use crate::parser::command_spec::{Identifier, NamedType, Type};
@ -39,10 +40,84 @@ impl Type {
} }
} }
pub fn mangle_result_name(generic_args: &[Type]) -> Ident {
assert_eq!(generic_args.len(), 2);
format_ident!(
"result_{}_{}",
generic_args[0].to_string(),
generic_args[1].to_string()
)
}
pub fn to_c_function(inputs: &[NamedType], output: &Option<Box<Type>>) -> TokenStream2 { pub fn to_c_function(inputs: &[NamedType], output: &Option<Box<Type>>) -> TokenStream2 {
Type::to_rust_function(&inputs, &output) Type::to_rust_function(&inputs, &output)
} }
pub fn to_c_result(generic_args: &[Type]) -> TokenStream2 {
assert_eq!(generic_args.len(), 2);
let first_generic = generic_args[0].to_c();
let second_generic = generic_args[1].to_c();
let ident = Type::mangle_result_name(&generic_args);
let value_ident = format_ident!("{}_value", ident);
quote! {
#[allow(non_camel_case_types)]
#[repr(C)]
pub union #value_ident {
ok: std::mem::ManuallyDrop<#first_generic>,
err: std::mem::ManuallyDrop<#second_generic>,
}
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct #ident {
tag: trixy::types::ResultTag,
value: #value_ident,
}
impl std::fmt::Debug for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.tag {
trixy::types::ResultTag::Ok => write!(f, "{:?}", unsafe { &self.value.ok }),
trixy::types::ResultTag::Err => write!(f, "{:?}", unsafe { &self.value.err }),
}
}
}
impl trixy::types::traits::convert_trait::Convertible
for crate::OurResult<#first_generic, #second_generic>
{
type Ptr = #ident;
fn into_ptr(self) -> Self::Ptr {
if self.value.is_ok() {
Self::Ptr {
tag: trixy::types::ResultTag::Ok,
value: #value_ident {
ok: std::mem::ManuallyDrop::new(self.value.expect("Is ok")),
},
}
} else {
Self::Ptr {
tag: trixy::types::ResultTag::Err,
value: #value_ident {
err: std::mem::ManuallyDrop::new(self.value.expect_err("Is err")),
},
}
}
}
fn from_ptr(_ptr: Self::Ptr) -> Result<Self, trixy::types::error::TypeConversionError> {
unreachable!("This should probably not be called?");
}
}
}
}
fn to_c_typical(identifier: &Identifier, generic_args: &Vec<Type>) -> TokenStream2 { fn to_c_typical(identifier: &Identifier, generic_args: &Vec<Type>) -> TokenStream2 {
let trixy_build_in_types: Vec<&str> = crate::types::BASE_TYPES let trixy_build_in_types: Vec<&str> = crate::types::BASE_TYPES
.iter() .iter()
@ -75,7 +150,7 @@ impl Type {
#nasp_path #ident #nasp_path #ident
} }
} else { } else {
debug_assert_eq!(trixy_build_in_types.len(), 1); assert_eq!(trixy_build_in_types.len(), 1);
let type_name = trixy_build_in_types let type_name = trixy_build_in_types
.first() .first()
@ -83,11 +158,28 @@ impl Type {
match *type_name { match *type_name {
"Result" => { "Result" => {
let ident_ok = &generic_args.first().expect("This is a result").to_c(); assert_eq!(generic_args.len(), 2);
let ident_err = &generic_args.last().expect("This is a result").to_c();
let namespaces_path = &identifier.variant.to_c_path();
let nasp_path = if namespaces_path.is_empty() {
quote! { quote! {
// eg: <Result<TrainedDog, TrainingMistake> as Convertible>::Ptr, crate ::
<Result<#ident_ok, #ident_err> as Convertible>::Ptr }
} else {
let path = namespaces_path;
quote! {
#path ::
}
};
let ident = format_ident!(
"result_{}_{}",
generic_args[0].to_string(),
generic_args[1].to_string()
);
quote! {
#nasp_path #ident
} }
} }
"Option" => { "Option" => {

View File

@ -45,13 +45,12 @@ impl Variant {
let main_namespace; let main_namespace;
match self { match self {
Variant::Structure { namespace } => { Variant::Structure { namespace }
| Variant::Enumeration { namespace }
| Variant::Result { namespace } => {
main_namespace = mangle_namespace_name(namespace); main_namespace = mangle_namespace_name(namespace);
} }
Variant::Enumeration { namespace } => { other => unreachable!("This should never be called on '{:#?}'", other),
main_namespace = mangle_namespace_name(namespace);
}
_ => unreachable!("This should never be called"),
} }
Namespace::to_rust_path_owned(&main_namespace[..]) Namespace::to_rust_path_owned(&main_namespace[..])
} }

View File

@ -36,10 +36,23 @@ pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
format!( format!(
"\ "\
/* C API */\n\ /* C API */\n\
// Workaround rust rules when implementing a foreign trait on a foreign type
#[derive(Debug)]
pub struct OurResult<T, E> {{
value: Result<T, E>,
}}
impl<T, E> From<Result<T, E>> for OurResult<T, E> {{
fn from(value: Result<T, E>) -> Self {{
Self {{ value }}
}}
}}
/* The real C API */\n\
{}", {}",
rust_code rust_code
) )
} }
/// This function generates the main c API provided by Trixy. /// This function generates the main c API provided by Trixy.
/// This works for example like this: /// This works for example like this:
/// Turning this: /// Turning this:

View File

@ -36,9 +36,14 @@ pub enum Variant {
Enumeration { Enumeration {
namespace: Vec<Identifier>, namespace: Vec<Identifier>,
}, },
Result {
namespace: Vec<Identifier>,
},
Namespace, Namespace,
/// The first (implicit) namespace, containing everything /// The first (implicit) namespace, containing everything
RootNamespace, RootNamespace,
Function, Function,
Primitive, Primitive,
NamedType, NamedType,
@ -59,7 +64,7 @@ impl Variant {
} }
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Namespace { pub struct Namespace {
pub name: Identifier, pub name: Identifier,
@ -89,21 +94,21 @@ impl From<Namespace> for CommandSpec {
} }
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Structure { pub struct Structure {
pub identifier: Identifier, pub identifier: Identifier,
pub contents: Vec<DocNamedType>, pub contents: Vec<DocNamedType>,
pub attributes: Vec<Attribute>, pub attributes: Vec<Attribute>,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Enumeration { pub struct Enumeration {
pub identifier: Identifier, pub identifier: Identifier,
pub states: Vec<DocIdentifier>, pub states: Vec<DocIdentifier>,
pub attributes: Vec<Attribute>, pub attributes: Vec<Attribute>,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Function { pub struct Function {
pub identifier: Identifier, pub identifier: Identifier,
pub inputs: Vec<NamedType>, pub inputs: Vec<NamedType>,
@ -206,7 +211,7 @@ pub struct NamedType {
pub r#type: Type, pub r#type: Type,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct DocNamedType { pub struct DocNamedType {
pub name: Identifier, pub name: Identifier,
pub r#type: Type, pub r#type: Type,

View File

@ -159,7 +159,7 @@ impl Parser {
let mut functions = vec![]; let mut functions = vec![];
for function in namespace.functions { for function in namespace.functions {
functions.push(self.process_function(function)?); functions.push(self.process_function(function, &previous_namespaces)?);
} }
let mut namespaces = vec![]; let mut namespaces = vec![];
for namespace in namespace.namespaces { for namespace in namespace.namespaces {
@ -187,14 +187,15 @@ impl Parser {
fn process_function( fn process_function(
&mut self, &mut self,
mut function: UncheckedFunction, mut function: UncheckedFunction,
parent_namespaces: &Vec<Identifier>,
) -> Result<Function, ParsingError> { ) -> Result<Function, ParsingError> {
let identifier = mem::take(&mut function.identifier.kind).to_identifier(Variant::Function); let identifier = mem::take(&mut function.identifier.kind).to_identifier(Variant::Function);
let mut inputs = vec![]; let mut inputs = vec![];
for input in function.inputs { for input in function.inputs {
inputs.push(self.process_named_type(input)?); inputs.push(self.process_named_type(input, parent_namespaces)?);
} }
let output = if let Some(r#type) = function.output { let output = if let Some(r#type) = function.output {
Some(self.process_type(r#type)?) Some(self.process_type(r#type, parent_namespaces)?)
} else { } else {
None None
}; };
@ -269,7 +270,7 @@ impl Parser {
}); });
let mut contents = vec![]; let mut contents = vec![];
for named_type in structure.contents { for named_type in structure.contents {
contents.push(self.process_doc_named_type(named_type)?); contents.push(self.process_doc_named_type(named_type, parent_namespaces)?);
} }
Ok(Structure { Ok(Structure {
@ -282,19 +283,21 @@ impl Parser {
fn process_named_type( fn process_named_type(
&mut self, &mut self,
mut named_type: UncheckedNamedType, mut named_type: UncheckedNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<NamedType, ParsingError> { ) -> Result<NamedType, ParsingError> {
let name: Identifier = let name: Identifier =
mem::take(&mut named_type.name.kind).to_identifier(Variant::NamedType); mem::take(&mut named_type.name.kind).to_identifier(Variant::NamedType);
let r#type: Type = self.process_type(named_type.r#type)?; let r#type: Type = self.process_type(named_type.r#type, parent_namespaces)?;
Ok(NamedType { name, r#type }) Ok(NamedType { name, r#type })
} }
fn process_doc_named_type( fn process_doc_named_type(
&mut self, &mut self,
mut doc_named_type: UncheckedDocNamedType, mut doc_named_type: UncheckedDocNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<DocNamedType, ParsingError> { ) -> Result<DocNamedType, ParsingError> {
let name: Identifier = let name: Identifier =
mem::take(&mut doc_named_type.name.kind).to_identifier(Variant::DocNamedType); mem::take(&mut doc_named_type.name.kind).to_identifier(Variant::DocNamedType);
let r#type: Type = self.process_type(doc_named_type.r#type)?; let r#type: Type = self.process_type(doc_named_type.r#type, parent_namespaces)?;
Ok(DocNamedType { Ok(DocNamedType {
name, name,
r#type, r#type,
@ -302,14 +305,18 @@ impl Parser {
}) })
} }
fn process_type(&mut self, r#type: UncheckedType) -> Result<Type, ParsingError> { fn process_type(
&mut self,
r#type: UncheckedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
match r#type { match r#type {
UncheckedType::Typical { UncheckedType::Typical {
identifier, identifier,
generic_args, generic_args,
} => self.process_typical_type(identifier, generic_args), } => self.process_typical_type(identifier, generic_args, parent_namespaces),
UncheckedType::Function { inputs, output } => { UncheckedType::Function { inputs, output } => {
self.process_function_type(inputs, output) self.process_function_type(inputs, output, parent_namespaces)
} }
} }
} }
@ -318,6 +325,7 @@ impl Parser {
&mut self, &mut self,
mut type_identifier: Token, mut type_identifier: Token,
generic_args: Vec<UncheckedType>, generic_args: Vec<UncheckedType>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> { ) -> Result<Type, ParsingError> {
fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec<Identifier> { fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec<Identifier> {
matches matches
@ -401,7 +409,13 @@ impl Parser {
.iter() .iter()
.any(|(ident, _)| ident == &identifier.name) .any(|(ident, _)| ident == &identifier.name)
{ {
if &identifier.name == "Result" {
variant = Variant::Result {
namespace: parent_namespaces.clone(),
};
} else {
variant = Variant::Primitive; variant = Variant::Primitive;
}
} else { } else {
return Err(ParsingError::TypeNotDeclared { return Err(ParsingError::TypeNotDeclared {
r#type: identifier, r#type: identifier,
@ -461,7 +475,7 @@ impl Parser {
let mut new_generic_args: Vec<checked::Type> = vec![]; let mut new_generic_args: Vec<checked::Type> = vec![];
for generic_arg in generic_args { for generic_arg in generic_args {
new_generic_args.push(self.process_type(generic_arg)?); new_generic_args.push(self.process_type(generic_arg, parent_namespaces)?);
} }
Ok(Type::Typical { Ok(Type::Typical {
@ -473,15 +487,16 @@ impl Parser {
&mut self, &mut self,
inputs: Vec<UncheckedNamedType>, inputs: Vec<UncheckedNamedType>,
output: Option<Box<UncheckedType>>, output: Option<Box<UncheckedType>>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> { ) -> Result<Type, ParsingError> {
let inputs = inputs let inputs = inputs
.into_iter() .into_iter()
.map(|input| self.process_named_type(input)) .map(|input| self.process_named_type(input, parent_namespaces))
.collect::<Result<Vec<NamedType>, ParsingError>>()?; .collect::<Result<Vec<NamedType>, ParsingError>>()?;
let mut new_output = None; let mut new_output = None;
if let Some(output) = output { if let Some(output) = output {
new_output = Some(Box::new(self.process_type(*output)?)); new_output = Some(Box::new(self.process_type(*output, parent_namespaces)?));
} }
Ok(Type::Function { Ok(Type::Function {

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TRIXY_RESULT_H
#define TRIXY_RESULT_H
/**
* @brief A rust-like result type
*
* @detail
* This macro effectively generates a type name from the two generic
* parameters. The generated type name is always globally unique.
*/
#define Result(ok, err) struct result_##ok##_##err
/**
* @brief The possibly states of a Result.
*/
enum ResultTag
{
OK,
ERR,
};
#endif // TRIXY_RESULT_H

View File

@ -40,7 +40,7 @@ macro_rules! header {
// NOTE: Every type here must have the [`Convertible`] implemented on it (@soispha) // NOTE: Every type here must have the [`Convertible`] implemented on it (@soispha)
// And be added to the `convert/c/auxiliary/idendentifier/mod.rs` file // And be added to the `convert/c/auxiliary/idendentifier/mod.rs` file
// And add it to the types list (`./types_list.rs`) // And add it to the types list (`./types_list.rs`)
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 11] = [ pub const BASE_TYPES: [(&'static std::primitive::str, usize); 12] = [
// Unsigned // Unsigned
("u8", 0), ("u8", 0),
("u16", 0), ("u16", 0),
@ -56,15 +56,19 @@ pub const BASE_TYPES: [(&'static std::primitive::str, usize); 11] = [
("f64", 0), ("f64", 0),
// Other // Other
("String", 0), ("String", 0),
("Result", 2),
// FIXME(@soispha): These work, but the generated code is not really ideal <2024-03-26> // FIXME(@soispha): These work, but the generated code is not really ideal <2024-03-26>
// ("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); 3] = pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 4] = [
[header!("errno.h"), header!("string.h"), header!("vec.h")]; header!("errno.h"),
header!("string.h"),
header!("vec.h"),
header!("result.h"),
];
pub fn header_names() -> std::string::String { pub fn header_names() -> std::string::String {
C_TYPE_HEADER C_TYPE_HEADER

View File

@ -129,121 +129,93 @@ pub extern "C" fn string_free(ptr: *const c_char) {
CString::drop_ptr(ptr); CString::drop_ptr(ptr);
} }
impl<T: Convertible> Convertible for Option<T> { // impl<T: Convertible> Convertible for Option<T> {
type Ptr = *const <T as Convertible>::Ptr; // type Ptr = *const <T as Convertible>::Ptr;
//
fn into_ptr(self) -> Self::Ptr { // fn into_ptr(self) -> Self::Ptr {
if let Some(inner) = self { // if let Some(inner) = self {
&inner.into_ptr() // &inner.into_ptr()
} else { // } else {
ptr::null() // ptr::null()
} // }
} // }
//
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> { // fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
warn!( // warn!(
" // "
This should never be called, as this option type resolves // This should never be called, as this option type resolves
to a null pointer or to a pointer to the real value // to a null pointer or to a pointer to the real value
(e. g. a `crate::Vec<T>`) // (e. g. a `crate::Vec<T>`)
" // "
); // );
Ok(Option::default()) // Ok(Option::default())
} // }
}
impl<T: Convertible, E: Error> Convertible for Result<T, E> {
type Ptr = *const <T as Convertible>::Ptr;
fn into_ptr(self) -> Self::Ptr {
match self {
Ok(ok) => &ok.into_ptr(),
Err(err) => {
errno::set(TypeConversionError::ResultWasErr {
original_message: err.to_string(),
});
ptr::null()
}
}
}
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
warn!(
"
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>`)
"
);
todo!()
// Ok(Result::Ok(T::default()))
}
}
impl<T: Convertible> Convertible for Vec<T> {
type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
fn into_ptr(self) -> Self::Ptr {
let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
let data_vec = ManuallyDrop::new(data_vec);
Self::Ptr {
data: data_vec.as_ptr(),
length: data_vec.len(),
capacity: data_vec.capacity(),
}
}
fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
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);
// } // }
let base_vec = unsafe {
// 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 as Convertible>::Ptr,
ptr.length,
ptr.capacity,
)
};
let vec: Vec<_> = base_vec // impl<T: Convertible> Convertible for Vec<T> {
.into_iter() // type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
.map(|val| <T as Convertible>::from_ptr(val)) //
.collect::<Result<Vec<_>, _>>()?; // fn into_ptr(self) -> Self::Ptr {
Ok(vec) // let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
} // let data_vec = ManuallyDrop::new(data_vec);
} // Self::Ptr {
// data: data_vec.as_ptr(),
macro_rules! make_vec_free { // length: data_vec.len(),
($value:ident, $name:ident) => { // capacity: data_vec.capacity(),
#[no_mangle] // }
pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) { // }
Vec::<$value>::drop_ptr(ptr); //
} // fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
}; // if ptr.data.is_null() {
} // return Err(TypeConversionError::NullPointer);
// }
// Unsigned // // TODO(@soispha): Add this, when this feature is stable <2024-02-17>
make_vec_free!(u8, vec_free_u8); // // if !ptr.data.is_aligned() {
make_vec_free!(u16, vec_free_u16); // // return Err(TypeConversionError::NotAligned);
make_vec_free!(u32, vec_free_u32); // // }
make_vec_free!(u64, vec_free_u64); // let base_vec = unsafe {
// Signed // // SAFETY:
make_vec_free!(i8, vec_free_i8); // // We simply hope that c treated our vector as read-only and didn't modify it.
make_vec_free!(i16, vec_free_i16); // // See the SAFETY section of the String implementation for more detail.
make_vec_free!(i32, vec_free_i32); // Vec::from_raw_parts(
make_vec_free!(i64, vec_free_i64); // ptr.data as *mut <T as Convertible>::Ptr,
// Float // ptr.length,
make_vec_free!(f32, vec_free_f32); // ptr.capacity,
make_vec_free!(f64, vec_free_f64); // )
// Other // };
make_vec_free!(String, vec_free_String); //
// let vec: Vec<_> = base_vec
// .into_iter()
// .map(|val| <T as Convertible>::from_ptr(val))
// .collect::<Result<Vec<_>, _>>()?;
// Ok(vec)
// }
// }
//
// macro_rules! make_vec_free {
// ($value:ident, $name:ident) => {
// #[no_mangle]
// pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) {
// Vec::<$value>::drop_ptr(ptr);
// }
// };
// }
//
// // Unsigned
// make_vec_free!(u8, vec_free_u8);
// make_vec_free!(u16, vec_free_u16);
// make_vec_free!(u32, vec_free_u32);
// make_vec_free!(u64, vec_free_u64);
// // Signed
// make_vec_free!(i8, vec_free_i8);
// make_vec_free!(i16, vec_free_i16);
// make_vec_free!(i32, vec_free_i32);
// make_vec_free!(i64, vec_free_i64);
// // Float
// make_vec_free!(f32, vec_free_f32);
// make_vec_free!(f64, vec_free_f64);
// // Other
// make_vec_free!(String, vec_free_String);
// FIXME(@soispha): Find a way to support these <2024-02-27> // FIXME(@soispha): Find a way to support these <2024-02-27>
// make_vec_free!(Option<?>, vec_free_Option); // make_vec_free!(Option<?>, vec_free_Option);

View File

@ -23,24 +23,30 @@
// 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>
// NOTE(@soispha): All types added here must also have an added free impl for the vector type (./traits/convert_trait.rs) <2024-02-27>
use std::ffi::c_char; use std::ffi::c_char;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[repr(C)] #[repr(C)]
pub struct String(pub(crate) *const c_char); pub struct String(pub(crate) *const c_char);
/// The tag used to tag the c result Enumerations
#[derive(Debug)] #[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct Vec<T> { pub enum ResultTag {
/// You should cast this value to it's supposed type (readable from the Trixy api definition Ok,
/// file) Err,
pub(crate) data: *const T,
pub(crate) length: usize,
pub(crate) capacity: usize,
} }
// #[derive(Debug)]
// #[repr(C)]
// 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,
// }
// Unsigned // Unsigned
pub use std::primitive::u16; pub use std::primitive::u16;
pub use std::primitive::u32; pub use std::primitive::u32;