feat(treewide): Finalize basic c API
See the example under `./example/main/` for more details.
This commit is contained in:
parent
e205ea4625
commit
e52f74b0c1
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
/result
|
||||
/dist
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "trixy-example"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
default-run = "main"
|
||||
|
||||
[dependencies]
|
||||
libloading = "0.8.1"
|
||||
trixy = { path = "../../." }
|
||||
|
||||
[build-dependencies]
|
||||
trixy = { path = "../../." }
|
|
@ -0,0 +1,35 @@
|
|||
BIN_NAME := ./target/plugin.so
|
||||
|
||||
BUILD_DIR := ./target/c_build/
|
||||
|
||||
SRC := $(wildcard c/*.c)
|
||||
OBJ := $(SRC:.c=.o)
|
||||
DEP := $(OBJ:.o=.d)
|
||||
|
||||
LIBS :=
|
||||
|
||||
ALL_CFLAGS := -O3 -MMD -Wall -Wextra -Wno-unused-parameter $(CFLAGS) $(CPPFLAGS)
|
||||
ALL_LDFLAGS := $(addprefix -l,$(LIBS)) -L $(LD_LIBRARY_PATH) $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
||||
$(BIN_NAME): $(OBJ)
|
||||
gcc $(addprefix $(BUILD_DIR),$(notdir $(OBJ))) -shared -o $(addprefix $(BUILD_DIR),$(notdir $(BIN_NAME))) $(ALL_CFLAGS) $(ALL_LDFLAGS)
|
||||
|
||||
$(OBJ): $(SRC)
|
||||
mkdir --parents $(BUILD_DIR)
|
||||
$(CC) -c -fPIC $< -o $(addprefix $(BUILD_DIR),$(notdir $(OBJ))) $(ALL_CFLAGS)
|
||||
|
||||
.PHONY : clean options
|
||||
options:
|
||||
@echo "CC = $(CC)"
|
||||
@echo "CFLAGS = $(ALL_CFLAGS)"
|
||||
@echo "LDFLAGS = $(ALL_LDFLAGS)"
|
||||
@echo "SRC = $(SRC)"
|
||||
@echo "OBJ = $(OBJ)"
|
||||
@echo "DEP = $(DEP)"
|
||||
@echo ""
|
||||
|
||||
clean :
|
||||
rm $(BIN_NAME) $(OBJ) $(DEP)
|
||||
rm -r $(BUILD_DIR)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# Reason
|
||||
This is not a cargo example, as its needs a full build.rs step.
|
|
@ -0,0 +1,11 @@
|
|||
use trixy::macros::config::TrixyConfig;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=./dist/*");
|
||||
println!("cargo:rerun-if-changed=./src/bin/main/api.tri");
|
||||
TrixyConfig::new("handle_cmd")
|
||||
.trixy_path("./src/bin/main/api.tri")
|
||||
.dist_dir_path("./dist")
|
||||
.generate_debug(true)
|
||||
.generate();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#include "../dist/interface.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define println(args...) \
|
||||
printf("[32;1m(plugin):[0m [34;1m"); \
|
||||
printf(args); \
|
||||
printf("\n[0m"); \
|
||||
fflush(stdout);
|
||||
#define eprintln(args...) \
|
||||
printf("[32;1m(plugin):[0m[31;1m "); \
|
||||
printf(args); \
|
||||
printf("\n[0m"); \
|
||||
fflush(stdout);
|
||||
|
||||
int p_main() {
|
||||
// You can simply call functions ..
|
||||
outstanding("James");
|
||||
|
||||
// .. but they check their inputs:
|
||||
// This call for example will fail, as a null pointer isn't supported.
|
||||
//
|
||||
// A error will be signaled by a 0 return code, then the error can be obtained
|
||||
// with the `last_error_length` and `last_error_message` functions
|
||||
if (!outstanding(0x0)) {
|
||||
int error_length = last_error_length();
|
||||
char *error = malloc(error_length);
|
||||
last_error_message(error, error_length);
|
||||
eprintln("Encountered error: %s", error);
|
||||
free(error);
|
||||
}
|
||||
|
||||
println("Saying hi!");
|
||||
string_t hi;
|
||||
if (!one.hi(&hi, "Adam")) {
|
||||
int error_length = last_error_length();
|
||||
char *error = malloc(error_length);
|
||||
last_error_message(error, error_length);
|
||||
eprintln("Encountered error: %s", error);
|
||||
free(error);
|
||||
}
|
||||
println("Rust returned: %s", hi);
|
||||
string_free(hi);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
let input = include_str!(concat!(env!("OUT_DIR"), "/api.rs"));
|
||||
println!("{}", input);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/// Call out an outstanding person
|
||||
fn outstanding(name: String);
|
||||
|
||||
|
||||
mod one {
|
||||
/// Say hi to a name
|
||||
fn hi(name: String) -> String;
|
||||
}
|
||||
|
||||
// Trixy is a subset of Rust
|
||||
// vim: syntax=rust cms=//%s
|
|
@ -0,0 +1,40 @@
|
|||
use std::{env, ffi::c_int, mem};
|
||||
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
||||
|
||||
// run `cargo r --bin api > ./src/bin/main/api.rs` to output the generated api
|
||||
// mod api;
|
||||
// pub use api::*;
|
||||
|
||||
fn handle_cmd(cmd: Commands) {
|
||||
match cmd {
|
||||
Commands::One(one) => match one {
|
||||
one::One::hi { trixy_output, name } => {
|
||||
let output = format!("Hi {}!", name);
|
||||
println!("(rust): {}", output);
|
||||
trixy_output.send(output.into()).expect("Will work");
|
||||
mem::forget(name);
|
||||
}
|
||||
},
|
||||
Commands::outstanding { name } => {
|
||||
println!("(rust): {} is outstanding!", name);
|
||||
mem::forget(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let library_path = env::args().nth(1).expect("USAGE: cargo r -- <LIB>");
|
||||
type AddFunc = unsafe fn() -> c_int;
|
||||
println!("Loading p_main() from {}", library_path);
|
||||
|
||||
unsafe {
|
||||
let lib = Library::new(library_path).unwrap();
|
||||
let func: Symbol<AddFunc> = lib.get(b"p_main").unwrap();
|
||||
println!("starting plugin");
|
||||
let out = func();
|
||||
println!("plugin finished with: {}", out);
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
# Copyright (C) 2023 The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
[package]
|
||||
name = "main-example"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
# [lib]
|
||||
# crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
trixy = { path = "../../../../trixy" }
|
||||
env_logger = { version = "0.10.1" }
|
||||
log = "0.4.20"
|
||||
libloading = "0.8.1"
|
||||
|
||||
[build-dependencies]
|
||||
trixy-macros = {path = "../../../trixy-macros"}
|
|
@ -1,35 +0,0 @@
|
|||
PREFIX := /usr/local
|
||||
BINPREFIX := $(DESTDIR)$(PREFIX)/bin
|
||||
MANPREFIX := $(DESTDIR)$(PREFIX)/share/man/man1
|
||||
|
||||
BIN_NAME := libmain.so
|
||||
|
||||
SRC := $(wildcard c_src/*.c)
|
||||
OBJ := $(SRC:.c=.o)
|
||||
DEP := $(OBJ:.o=.d)
|
||||
|
||||
LIBS := main-example
|
||||
|
||||
ALL_CFLAGS := -fPIC -O3 -MMD -Wall -Wextra -Wno-unused-parameter $(CFLAGS) $(CPPFLAGS)
|
||||
ALL_LDFLAGS := -shared $(addprefix -l,$(LIBS)) -L $(LD_LIBRARY_PATH) $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
||||
|
||||
$(BIN_NAME): $(OBJ)
|
||||
$(CC) -o $@ $+ $(ALL_LDFLAGS)
|
||||
|
||||
.c.o:
|
||||
$(CC) -o $@ $< -c $(ALL_CFLAGS)
|
||||
|
||||
.PHONY : clean options
|
||||
options:
|
||||
@echo "CC = $(CC)"
|
||||
@echo "CFLAGS = $(ALL_CFLAGS)"
|
||||
@echo "LDFLAGS = $(ALL_LDFLAGS)"
|
||||
@echo "SRC = $(SRC)"
|
||||
@echo "OBJ = $(OBJ)"
|
||||
@echo "DEP = $(DEP)"
|
||||
@echo ""
|
||||
|
||||
clean :
|
||||
rm $(BIN_NAME) $(OBJ) $(DEP)
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use trixy_macros::config::TrixyConfig;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-arg=--relocatable");
|
||||
println!("cargo:rerun-if-changed=./src/api.tri");
|
||||
TrixyConfig::new("callback")
|
||||
.trixy_path("./src/api.tri")
|
||||
.dist_dir_path("./dist")
|
||||
.generate_debug(true)
|
||||
.generate();
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Trinitrix Project <soispha@vhack.eu,
|
||||
* antifallobst@systemausfall.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "../dist/interface.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
// You can simply call functions ..
|
||||
fn_alone("hi");
|
||||
|
||||
// .. but they check their inputs:
|
||||
// This call for example will fail, as a null pointer isn't supported.
|
||||
//
|
||||
// A error will be signaled by a 0 return code, then the error can be obtained
|
||||
// with the `last_error_length` and `last_error_message` functions
|
||||
if (!fn_alone(0x0)) {
|
||||
int error_length = last_error_length();
|
||||
char *error = malloc(error_length);
|
||||
last_error_message(error, error_length);
|
||||
printf("Encountered error: %s\n", error);
|
||||
free(error);
|
||||
}
|
||||
|
||||
one.fn_one();
|
||||
// one.two.fn_two();
|
||||
//
|
||||
// two_t two = one.two;
|
||||
// two.three.fn_three();
|
||||
// one.two.three.fn_three();
|
||||
// one_two_fn_two();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
// nasp trinitrix {
|
||||
// struct Callback {
|
||||
// func: String,
|
||||
// timeout: String,
|
||||
// };
|
||||
//
|
||||
// enum CallbackPriority {
|
||||
// High,
|
||||
// Medium,
|
||||
// Low,
|
||||
// };
|
||||
//
|
||||
// fn execute_callback(callback: Callback, priority: CallbackPriority);
|
||||
// }
|
||||
|
||||
/// Some doc comment
|
||||
fn fn_alone(input: String);
|
||||
/// Some doc comment
|
||||
nasp one {
|
||||
/// Some doc comment
|
||||
fn fn_one();
|
||||
nasp two {
|
||||
fn fn_two();
|
||||
nasp three {
|
||||
fn fn_three();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||
// vim: syntax=rust
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use std::ffi::c_int;
|
||||
macro_rules! callback {
|
||||
($cmd:expr) => {{
|
||||
println!("{:#?}", $cmd);
|
||||
}};
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
||||
|
||||
fn main() {
|
||||
let out = unsafe {
|
||||
let lib = libloading::Library::new("./libmain.so").unwrap();
|
||||
let func: libloading::Symbol<unsafe extern "C" fn() -> c_int> = lib.get(b"main").unwrap();
|
||||
func()
|
||||
};
|
||||
|
||||
assert_eq!(out, 0);
|
||||
}
|
|
@ -54,7 +54,7 @@ fn structure_to_header(structure: &Structure) -> String {
|
|||
format!(
|
||||
"
|
||||
{}
|
||||
struct {} {{
|
||||
{} {{
|
||||
{}
|
||||
}};",
|
||||
doc_comments, ident, contents
|
||||
|
@ -94,7 +94,7 @@ fn enumeration_to_header(enumeration: &Enumeration) -> String {
|
|||
format!(
|
||||
"
|
||||
{}
|
||||
enum {} {{
|
||||
{} {{
|
||||
{}
|
||||
}};",
|
||||
doc_comments, ident, states
|
||||
|
|
|
@ -19,7 +19,7 @@ fn namespace_to_full_struct_init(nasp: &Namespace, namespaces: &Vec<&Identifier>
|
|||
input_namespaces.push(&nasp.name);
|
||||
|
||||
let ident = identifier_to_rust(&nasp.name);
|
||||
let type_ident = format_ident!("{}_t", ident.to_string());
|
||||
let type_ident = format_ident!("{}", ident.to_string());
|
||||
let functions: TokenStream2 = nasp
|
||||
.functions
|
||||
.iter()
|
||||
|
@ -40,7 +40,7 @@ fn namespace_to_full_struct_init(nasp: &Namespace, namespaces: &Vec<&Identifier>
|
|||
quote! {
|
||||
#next_namespace
|
||||
|
||||
const #type_ident #ident = {
|
||||
const struct #type_ident #ident = {
|
||||
#functions
|
||||
#namespaces
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
function_identifier_to_rust, identifier_to_rust, type_to_rust,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -117,13 +117,16 @@ fn function_to_c(
|
|||
let output_ident = type_to_c_equalivalent(&r#type, &namespaces);
|
||||
quote! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn #ident(output: *mut #output_ident, #(#inputs),*) -> core::ffi::c_int {
|
||||
pub extern "C" fn #ident(output: *mut #output_ident, #(#inputs),*) -> core::ffi::c_int {
|
||||
let output_val: #output_ident = {
|
||||
let (tx, rx) = trixy::oneshot::channel();
|
||||
#callback_function (#command_value);
|
||||
rx.recv().expect("The channel should not be close until this value is received").into()
|
||||
let recv = rx.recv().expect("The channel should not be closed until this value is received");
|
||||
recv.into()
|
||||
};
|
||||
output.write(output_val);
|
||||
unsafe {
|
||||
std::ptr::write(output, output_val);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +221,13 @@ fn function_path_to_rust(namespaces: &Vec<&Identifier>, function: &Function) ->
|
|||
fn named_type_to_rust_assignment(named_type: &NamedType) -> TokenStream2 {
|
||||
let ident = identifier_to_rust(&named_type.name);
|
||||
quote! {
|
||||
#ident : trixy::types::convert!(#ident)
|
||||
#ident : match #ident.try_into() {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
trixy::types::traits::errno::set(err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::{Ident, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote};
|
||||
use trixy_parser::command_spec::{Function, Identifier, Type};
|
||||
use trixy_parser::command_spec::{Function, Identifier, Type, Variant};
|
||||
|
||||
use super::identifier_to_rust;
|
||||
|
||||
|
@ -57,11 +57,30 @@ pub fn type_to_c(r#type: &Type, is_output: bool) -> TokenStream2 {
|
|||
}
|
||||
}
|
||||
pub fn identifier_to_c(identifier: &Identifier) -> TokenStream2 {
|
||||
match identifier.variant {
|
||||
Variant::Structure => {
|
||||
let ident = format_ident!("{}", identifier.name.to_case(Case::Pascal));
|
||||
quote! {
|
||||
struct #ident
|
||||
}
|
||||
}
|
||||
Variant::Enumeration => {
|
||||
let ident = format_ident!("{}", identifier.name.to_case(Case::Pascal));
|
||||
quote! {
|
||||
enum #ident
|
||||
}
|
||||
}
|
||||
Variant::Primitive => {
|
||||
let ident = format_ident!("{}_t", identifier.name.to_case(Case::Snake));
|
||||
quote! {
|
||||
#ident
|
||||
}
|
||||
}
|
||||
other => {
|
||||
unimplemented!("{:#?}", other)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn type_to_c_equalivalent(r#type: &Type, namespaces: &[&Identifier]) -> TokenStream2 {
|
||||
let ident = identifier_to_rust(&r#type.identifier);
|
||||
let trixy_build_in_types: Vec<&str> = trixy_types::BASE_TYPES
|
||||
|
|
|
@ -253,7 +253,7 @@ fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
|
|||
#[allow(non_camel_case_types)]
|
||||
#debug
|
||||
#[repr(C)]
|
||||
#[derive(Convertible, TypeInfo)]
|
||||
#[derive(Convertible)]
|
||||
pub enum #ident {
|
||||
#(#states),*
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ fn structure_to_rust(structure: &Structure) -> TokenStream2 {
|
|||
#[allow(non_camel_case_types)]
|
||||
#debug
|
||||
#[repr(C)]
|
||||
#[derive(Convertible, TypeInfo)]
|
||||
#[derive(Convertible)]
|
||||
pub struct #ident {
|
||||
#(#contents),*
|
||||
}
|
||||
|
|
|
@ -26,6 +26,29 @@ use crate::lexing::TokenKind;
|
|||
|
||||
use super::unchecked;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum Variant {
|
||||
Structure,
|
||||
Enumeration,
|
||||
Namespace,
|
||||
Function,
|
||||
NamedType,
|
||||
Primitive,
|
||||
DocNamedType,
|
||||
Void,
|
||||
}
|
||||
|
||||
impl Default for Variant {
|
||||
fn default() -> Self {
|
||||
Self::Void
|
||||
}
|
||||
}
|
||||
impl Variant {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Namespace {
|
||||
pub name: Identifier,
|
||||
|
@ -128,16 +151,20 @@ impl From<&DocNamedType> for NamedType {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<TokenKind> for Identifier {
|
||||
fn from(value: TokenKind) -> Self {
|
||||
match value {
|
||||
TokenKind::Identifier(ident) => Identifier { name: ident },
|
||||
impl TokenKind {
|
||||
pub fn to_identifier(self: TokenKind, variant: Variant) -> Identifier {
|
||||
match self {
|
||||
TokenKind::Identifier(ident) => Identifier {
|
||||
name: ident,
|
||||
variant,
|
||||
},
|
||||
_ => {
|
||||
panic!(
|
||||
"Tried to convert a non Identifier TokenKind to a Identefier. This is a bug
|
||||
Token was: '{}'
|
||||
"
|
||||
Tried to convert a non Identifier TokenKind to a
|
||||
Identifier. This is a bug. The token was: '{}'
|
||||
",
|
||||
value
|
||||
self
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -166,11 +193,13 @@ impl From<unchecked::Attribute> for Attribute {
|
|||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct Identifier {
|
||||
pub name: String,
|
||||
pub variant: Variant,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct DocIdentifier {
|
||||
pub name: String,
|
||||
pub variant: Variant,
|
||||
pub attributes: Vec<Attribute>,
|
||||
}
|
||||
|
||||
|
@ -178,6 +207,7 @@ impl From<&DocIdentifier> for Identifier {
|
|||
fn from(value: &DocIdentifier) -> Self {
|
||||
Self {
|
||||
name: value.name.to_owned(),
|
||||
variant: value.variant,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ use crate::{
|
|||
NamedType as UncheckedNamedType, Namespace as UncheckedNamespace,
|
||||
Structure as UncheckedStructure, Type as UncheckedType,
|
||||
},
|
||||
Variant,
|
||||
},
|
||||
error::ErrorContext,
|
||||
lexing::{TokenKind, TokenSpan},
|
||||
|
@ -92,10 +93,14 @@ impl Parser {
|
|||
) -> Result<Namespace, ParsingError> {
|
||||
let namespace_span = namespace.name.span;
|
||||
let name = match namespace.name.kind {
|
||||
TokenKind::Identifier(ident) => Identifier { name: ident },
|
||||
TokenKind::Identifier(ident) => Identifier {
|
||||
name: ident,
|
||||
variant: Variant::Namespace,
|
||||
},
|
||||
// This is not really used, so the value put here does not matter
|
||||
TokenKind::Dummy => Identifier {
|
||||
name: "".to_owned(),
|
||||
variant: Variant::Void,
|
||||
},
|
||||
_ => unreachable!("This should never be more than these two enum veriants"),
|
||||
};
|
||||
|
@ -144,7 +149,7 @@ impl Parser {
|
|||
&mut self,
|
||||
mut function: UncheckedFunction,
|
||||
) -> Result<Function, ParsingError> {
|
||||
let identifier = mem::take(&mut function.identifier.kind).into();
|
||||
let identifier = mem::take(&mut function.identifier.kind).to_identifier(Variant::Function);
|
||||
let mut inputs = vec![];
|
||||
for input in function.inputs {
|
||||
inputs.push(self.process_named_type(input)?);
|
||||
|
@ -172,7 +177,8 @@ impl Parser {
|
|||
self.enumerations.push(enumeration.clone());
|
||||
|
||||
let enum_span = enumeration.identifier.span;
|
||||
let identifier: Identifier = mem::take(&mut enumeration.identifier.kind).into();
|
||||
let identifier: Identifier =
|
||||
mem::take(&mut enumeration.identifier.kind).to_identifier(Variant::Enumeration);
|
||||
if &identifier == namespace_name {
|
||||
return Err(ParsingError::EnumWithNamespaceName {
|
||||
name: identifier.clone(),
|
||||
|
@ -191,10 +197,12 @@ impl Parser {
|
|||
let mut states = vec![];
|
||||
for mut state in enumeration.states {
|
||||
states.push({
|
||||
let ident: Identifier = mem::take(&mut state.token.kind).into();
|
||||
let ident: Identifier =
|
||||
mem::take(&mut state.token.kind).to_identifier(Variant::DocNamedType);
|
||||
DocIdentifier {
|
||||
name: ident.name,
|
||||
attributes: pass_attrs_along!(state),
|
||||
variant: Variant::DocNamedType,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -212,7 +220,8 @@ impl Parser {
|
|||
) -> Result<Structure, ParsingError> {
|
||||
self.structures.push(structure.clone());
|
||||
|
||||
let identifier: Identifier = mem::take(&mut structure.identifier.kind).into();
|
||||
let identifier: Identifier =
|
||||
mem::take(&mut structure.identifier.kind).to_identifier(Variant::Structure);
|
||||
let mut contents = vec![];
|
||||
for named_type in structure.contents {
|
||||
contents.push(self.process_doc_named_type(named_type)?);
|
||||
|
@ -229,7 +238,8 @@ impl Parser {
|
|||
&mut self,
|
||||
mut named_type: UncheckedNamedType,
|
||||
) -> Result<NamedType, ParsingError> {
|
||||
let name: Identifier = mem::take(&mut named_type.name.kind).into();
|
||||
let name: Identifier =
|
||||
mem::take(&mut named_type.name.kind).to_identifier(Variant::NamedType);
|
||||
let r#type: Type = self.process_type(named_type.r#type)?;
|
||||
Ok(NamedType { name, r#type })
|
||||
}
|
||||
|
@ -237,7 +247,8 @@ impl Parser {
|
|||
&mut self,
|
||||
mut doc_named_type: UncheckedDocNamedType,
|
||||
) -> Result<DocNamedType, ParsingError> {
|
||||
let name: Identifier = mem::take(&mut doc_named_type.name.kind).into();
|
||||
let name: Identifier =
|
||||
mem::take(&mut doc_named_type.name.kind).to_identifier(Variant::DocNamedType);
|
||||
let r#type: Type = self.process_type(doc_named_type.r#type)?;
|
||||
Ok(DocNamedType {
|
||||
name,
|
||||
|
@ -247,27 +258,40 @@ impl Parser {
|
|||
}
|
||||
|
||||
fn process_type(&mut self, mut r#type: UncheckedType) -> Result<Type, ParsingError> {
|
||||
let identifier: Identifier = mem::take(&mut r#type.identifier.kind).into();
|
||||
let identifier: Identifier =
|
||||
mem::take(&mut r#type.identifier.kind).to_identifier(Variant::Void);
|
||||
|
||||
if !self
|
||||
let variant: Variant;
|
||||
if self
|
||||
.structures
|
||||
.iter()
|
||||
.map(|r#struct| Into::<Identifier>::into(r#struct.identifier.kind.clone()))
|
||||
.map(|r#struct| (r#struct.identifier.kind.clone()).to_identifier(Variant::Void))
|
||||
.any(|ident| ident == identifier)
|
||||
&& !self
|
||||
{
|
||||
variant = Variant::Structure;
|
||||
} else if self
|
||||
.enumerations
|
||||
.iter()
|
||||
.map(|r#enum| Into::<Identifier>::into(r#enum.identifier.kind.clone()))
|
||||
.map(|r#enum| (r#enum.identifier.kind.clone()).to_identifier(Variant::Void))
|
||||
.any(|ident| ident == identifier)
|
||||
&& !BASE_TYPES
|
||||
{
|
||||
variant = Variant::Enumeration;
|
||||
} else if BASE_TYPES
|
||||
.iter()
|
||||
.any(|(ident, _)| ident == &identifier.name)
|
||||
{
|
||||
variant = Variant::Primitive;
|
||||
} else {
|
||||
return Err(ParsingError::TypeNotDeclared {
|
||||
r#type: identifier,
|
||||
span: r#type.identifier.span,
|
||||
});
|
||||
}
|
||||
let identifier: Identifier = Identifier {
|
||||
name: identifier.name,
|
||||
variant,
|
||||
};
|
||||
|
||||
{
|
||||
let fitting_types: Vec<&usize> = BASE_TYPES
|
||||
.iter()
|
||||
|
|
|
@ -22,6 +22,7 @@ use crate::command_spec::checked::{
|
|||
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
||||
NamedType, Namespace, Structure, Type,
|
||||
};
|
||||
use crate::command_spec::Variant;
|
||||
use crate::lexing::TokenStream;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
@ -59,19 +60,23 @@ mod trinitrix {
|
|||
namespaces: vec![Namespace {
|
||||
name: Identifier {
|
||||
name: "trinitrix".to_owned(),
|
||||
variant: Variant::Namespace,
|
||||
},
|
||||
functions: vec![Function {
|
||||
identifier: Identifier {
|
||||
name: "execute_callback".to_owned(),
|
||||
variant: Variant::Function,
|
||||
},
|
||||
inputs: vec![
|
||||
NamedType {
|
||||
name: Identifier {
|
||||
name: "callback".to_owned(),
|
||||
variant: Variant::NamedType,
|
||||
},
|
||||
r#type: Type {
|
||||
identifier: Identifier {
|
||||
name: "Callback".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
},
|
||||
|
@ -79,10 +84,12 @@ mod trinitrix {
|
|||
NamedType {
|
||||
name: Identifier {
|
||||
name: "priority".to_owned(),
|
||||
variant: Variant::NamedType,
|
||||
},
|
||||
r#type: Type {
|
||||
identifier: Identifier {
|
||||
name: "CallbackPriority".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
},
|
||||
|
@ -94,15 +101,18 @@ mod trinitrix {
|
|||
structures: vec![Structure {
|
||||
identifier: Identifier {
|
||||
name: "Callback".to_owned(),
|
||||
variant: Variant::Structure,
|
||||
},
|
||||
contents: vec![
|
||||
DocNamedType {
|
||||
name: Identifier {
|
||||
name: "func".to_owned(),
|
||||
variant: Variant::DocNamedType,
|
||||
},
|
||||
r#type: Type {
|
||||
identifier: Identifier {
|
||||
name: "()".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
},
|
||||
|
@ -111,10 +121,12 @@ mod trinitrix {
|
|||
DocNamedType {
|
||||
name: Identifier {
|
||||
name: "timeout".to_owned(),
|
||||
variant: Variant::DocNamedType,
|
||||
},
|
||||
r#type: Type {
|
||||
identifier: Identifier {
|
||||
name: "u8".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
},
|
||||
|
@ -126,19 +138,23 @@ mod trinitrix {
|
|||
enumerations: vec![Enumeration {
|
||||
identifier: Identifier {
|
||||
name: "CallbackPriority".to_owned(),
|
||||
variant: Variant::Enumeration,
|
||||
},
|
||||
states: vec![
|
||||
DocIdentifier {
|
||||
name: "High".to_owned(),
|
||||
attributes: vec![],
|
||||
variant: Variant::DocNamedType,
|
||||
},
|
||||
DocIdentifier {
|
||||
name: "Medium".to_owned(),
|
||||
attributes: vec![],
|
||||
variant: Variant::DocNamedType,
|
||||
},
|
||||
DocIdentifier {
|
||||
name: "Low".to_owned(),
|
||||
attributes: vec![],
|
||||
variant: Variant::DocNamedType,
|
||||
},
|
||||
],
|
||||
attributes: vec![],
|
||||
|
@ -171,7 +187,8 @@ fn execute_callback(callback: Name);
|
|||
assert_eq!(
|
||||
r#type,
|
||||
Identifier {
|
||||
name: "Name".to_owned()
|
||||
name: "Name".to_owned(),
|
||||
variant: Variant::Type,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -202,14 +219,17 @@ mod trinitrix {
|
|||
functions: vec![Function {
|
||||
identifier: Identifier {
|
||||
name: "print".to_owned(),
|
||||
variant: Variant::Function,
|
||||
},
|
||||
inputs: vec![NamedType {
|
||||
name: Identifier {
|
||||
name: "message".to_owned(),
|
||||
variant: Variant::NamedType,
|
||||
},
|
||||
r#type: Type {
|
||||
identifier: Identifier {
|
||||
name: "String".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
},
|
||||
|
@ -220,18 +240,22 @@ mod trinitrix {
|
|||
namespaces: vec![Namespace {
|
||||
name: Identifier {
|
||||
name: "trinitrix".to_owned(),
|
||||
variant: Variant::Namespace,
|
||||
},
|
||||
functions: vec![Function {
|
||||
identifier: Identifier {
|
||||
name: "hi".to_owned(),
|
||||
variant: Variant::Function,
|
||||
},
|
||||
inputs: vec![NamedType {
|
||||
name: Identifier {
|
||||
name: "name".to_owned(),
|
||||
variant: Variant::NamedType,
|
||||
},
|
||||
r#type: Type {
|
||||
identifier: Identifier {
|
||||
name: "String".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
},
|
||||
|
@ -239,6 +263,7 @@ mod trinitrix {
|
|||
output: Some(Type {
|
||||
identifier: Identifier {
|
||||
name: "String".to_owned(),
|
||||
variant: Variant::Type,
|
||||
},
|
||||
generic_args: vec![],
|
||||
}),
|
||||
|
|
|
@ -30,3 +30,4 @@ 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"}
|
||||
|
|
|
@ -27,6 +27,9 @@ pub enum TypeConversionError {
|
|||
#[error("Can't convert this ('{got:#?}') string into a valid rust string; Remember that these must be valid utf-8")]
|
||||
String { got: *const c_char },
|
||||
|
||||
#[error("Failed to parse this cstring into a rust string (which are valid utf-8)")]
|
||||
CString(#[from] std::ffi::IntoStringError),
|
||||
|
||||
#[error("You passed a null pointer to the conversion function!")]
|
||||
NullPointer,
|
||||
|
||||
|
@ -38,4 +41,7 @@ pub enum TypeConversionError {
|
|||
|
||||
#[error("The returned Result was an error: {original_message}")]
|
||||
ResultWasErr { original_message: String },
|
||||
|
||||
#[error("This funciton is not admissable")]
|
||||
NotAdmissable,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::error::{self, TypeConversionError};
|
||||
use log::warn;
|
||||
use std::{
|
||||
error::Error,
|
||||
ffi::{c_char, CString},
|
||||
|
@ -8,14 +9,24 @@ use std::{
|
|||
|
||||
use super::errno;
|
||||
|
||||
pub use trixy_types_derive::Convertible;
|
||||
|
||||
/// Convert a value into a data structure that we can send directly to c.
|
||||
pub trait Convertible {
|
||||
pub trait Convertible
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
type Ptr;
|
||||
/// Turn the value into a c void pointer or a data structure that c can understand.
|
||||
fn into_ptr(self) -> Self::Ptr;
|
||||
|
||||
fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError>;
|
||||
|
||||
/// 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>;
|
||||
fn drop_ptr(ptr: Self::Ptr) {
|
||||
let val = Self::from_ptr(ptr);
|
||||
drop(val);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! primitive_convert {
|
||||
|
@ -27,8 +38,9 @@ macro_rules! primitive_convert {
|
|||
self
|
||||
}
|
||||
|
||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||
unreachable!("Theres is no need to drop a {}", stringify!($name))
|
||||
fn from_ptr(_: Self::Ptr) -> Result<$name, error::TypeConversionError> {
|
||||
warn!("Dropping a {}; Theres is no need to drop a {}", stringify!($name), stringify!($name));
|
||||
Ok(<$name>::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,14 +63,13 @@ primitive_convert!(f32);
|
|||
primitive_convert!(f64);
|
||||
|
||||
impl Convertible for CString {
|
||||
type Ptr = *const *const c_char;
|
||||
type Ptr = *const c_char;
|
||||
|
||||
fn into_ptr(self) -> Self::Ptr {
|
||||
self.into_raw() as *const *const c_char
|
||||
self.into_raw() as *const c_char
|
||||
}
|
||||
|
||||
fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||
unsafe {
|
||||
fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
|
||||
if ptr.is_null() {
|
||||
return Err(TypeConversionError::NullPointer);
|
||||
}
|
||||
|
@ -73,11 +84,29 @@ impl Convertible for CString {
|
|||
// 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);
|
||||
let string = unsafe { CString::from_raw(ptr as *mut i8) };
|
||||
Ok(string)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Convertible for std::string::String {
|
||||
type Ptr = <CString as Convertible>::Ptr;
|
||||
|
||||
fn into_ptr(self) -> Self::Ptr {
|
||||
let c_string =
|
||||
CString::new(self).expect("This will always work; the input value is a valid string");
|
||||
let ptr: Self::Ptr = c_string.into_ptr();
|
||||
ptr
|
||||
}
|
||||
|
||||
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
|
||||
unreachable!("This should not be called, call CString directly");
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn string_free(ptr: *const c_char) {
|
||||
CString::drop_ptr(ptr);
|
||||
}
|
||||
|
||||
impl<T: Convertible> Convertible for Option<T> {
|
||||
|
@ -92,14 +121,15 @@ impl<T: Convertible> Convertible for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||
unreachable!(
|
||||
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
|
||||
warn!(
|
||||
"
|
||||
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>`)
|
||||
"
|
||||
);
|
||||
Ok(Option::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,14 +151,16 @@ impl<T: Convertible, E: Error> Convertible for Result<T, E> {
|
|||
}
|
||||
}
|
||||
|
||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||
unreachable!(
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,23 +176,26 @@ impl<T> Convertible for Vec<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
||||
let vec = unsafe {
|
||||
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 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, ptr.length, ptr.capacity)
|
||||
};
|
||||
drop(vec);
|
||||
Ok(())
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(@soispha): Add the required generic support here <2024-02-18>
|
||||
#[no_mangle]
|
||||
pub extern "C" fn vec_free(ptr: crate::Vec<u8>) {
|
||||
Vec::drop_ptr(ptr);
|
||||
}
|
||||
|
|
|
@ -30,19 +30,6 @@ use log::{error, warn};
|
|||
|
||||
use crate::error::TypeConversionError;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! convert {
|
||||
($input:expr) => {
|
||||
match $input.try_into() {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
trixy::types::traits::errno::set(err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This code is heavily inspired by: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html
|
||||
thread_local! {
|
||||
static LAST_ERROR: RefCell<Option<Box<TypeConversionError>>> = RefCell::new(None);
|
||||
|
|
|
@ -20,3 +20,5 @@
|
|||
|
||||
pub mod convert_trait;
|
||||
pub mod errno;
|
||||
mod try_from_impl;
|
||||
pub use try_from_impl::*;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use crate::String;
|
||||
use std::ffi::CString;
|
||||
|
||||
use super::convert_trait::Convertible;
|
||||
|
||||
impl From<std::string::String> for String {
|
||||
fn from(value: std::string::String) -> Self {
|
||||
let cstring =
|
||||
CString::new(value).expect("The input is a valid String, this always succeeds");
|
||||
let c_char = cstring.into_ptr();
|
||||
String(c_char)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for std::string::String {
|
||||
type Error = crate::error::TypeConversionError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
let cstring = CString::from_ptr(value.0)?;
|
||||
let string = cstring.into_string()?;
|
||||
Ok(string)
|
||||
}
|
||||
}
|
|
@ -1,6 +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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct String(pub(crate) *const c_char);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Vec<T> {
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
# build
|
||||
/target
|
||||
/result
|
||||
/dist
|
||||
|
||||
# C stuff
|
||||
*.d
|
||||
*.o
|
||||
|
||||
# This crate is a library
|
||||
Cargo.lock
|
|
@ -0,0 +1,14 @@
|
|||
[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"
|
||||
proc-macro2 = "1.0.78"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
|
@ -0,0 +1,67 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{Fields, FieldsNamed};
|
||||
|
||||
#[proc_macro_derive(Convertible)]
|
||||
pub fn convertible_derive(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let name = &ast.ident;
|
||||
|
||||
let drop_impl = match ast.data {
|
||||
syn::Data::Struct(stru) => {
|
||||
let field_drop = if let Fields::Named(FieldsNamed {
|
||||
brace_token: _,
|
||||
named,
|
||||
}) = stru.fields
|
||||
{
|
||||
named
|
||||
.iter()
|
||||
.map(|field| field.ident.as_ref().expect("All are under NamedFields"))
|
||||
.map(|name| {
|
||||
quote! {
|
||||
self.#name.drop_ptr()?;
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream2>()
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl trixy::types::traits::convert_trait::Convertible for #name {
|
||||
type Ptr = #name;
|
||||
|
||||
fn into_ptr(self) -> Self::Ptr {
|
||||
self
|
||||
}
|
||||
|
||||
fn from_ptr(ptr: Self::Ptr) -> Result<Self, trixy::types::error::TypeConversionError> {
|
||||
// #field_drop
|
||||
// Ok(())
|
||||
Err(trixy::types::error::TypeConversionError::NotAdmissable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Data::Enum(_) => {
|
||||
quote! {
|
||||
impl trixy::types::traits::convert_trait::Convertible for #name {
|
||||
type Ptr = #name ;
|
||||
|
||||
fn into_ptr(self) -> Self::Ptr {
|
||||
self
|
||||
}
|
||||
|
||||
fn from_ptr(ptr: Self::Ptr) -> Result<Self, trixy::types::error::TypeConversionError> {
|
||||
// log::warn!("Drop does nothing on enums! (Called for {})", stringify!(#name));
|
||||
Err(trixy::types::error::TypeConversionError::NotAdmissable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Data::Union(_) => panic!("This derive macro does nothing for unions"),
|
||||
};
|
||||
|
||||
drop_impl.into()
|
||||
}
|
Reference in New Issue