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!(
|
format!(
|
||||||
"
|
"
|
||||||
{}
|
{}
|
||||||
struct {} {{
|
{} {{
|
||||||
{}
|
{}
|
||||||
}};",
|
}};",
|
||||||
doc_comments, ident, contents
|
doc_comments, ident, contents
|
||||||
|
@ -94,7 +94,7 @@ fn enumeration_to_header(enumeration: &Enumeration) -> String {
|
||||||
format!(
|
format!(
|
||||||
"
|
"
|
||||||
{}
|
{}
|
||||||
enum {} {{
|
{} {{
|
||||||
{}
|
{}
|
||||||
}};",
|
}};",
|
||||||
doc_comments, ident, states
|
doc_comments, ident, states
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn namespace_to_full_struct_init(nasp: &Namespace, namespaces: &Vec<&Identifier>
|
||||||
input_namespaces.push(&nasp.name);
|
input_namespaces.push(&nasp.name);
|
||||||
|
|
||||||
let ident = identifier_to_rust(&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
|
let functions: TokenStream2 = nasp
|
||||||
.functions
|
.functions
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -40,7 +40,7 @@ fn namespace_to_full_struct_init(nasp: &Namespace, namespaces: &Vec<&Identifier>
|
||||||
quote! {
|
quote! {
|
||||||
#next_namespace
|
#next_namespace
|
||||||
|
|
||||||
const #type_ident #ident = {
|
const struct #type_ident #ident = {
|
||||||
#functions
|
#functions
|
||||||
#namespaces
|
#namespaces
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
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);
|
let output_ident = type_to_c_equalivalent(&r#type, &namespaces);
|
||||||
quote! {
|
quote! {
|
||||||
#[no_mangle]
|
#[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 output_val: #output_ident = {
|
||||||
let (tx, rx) = trixy::oneshot::channel();
|
let (tx, rx) = trixy::oneshot::channel();
|
||||||
#callback_function (#command_value);
|
#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;
|
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 {
|
fn named_type_to_rust_assignment(named_type: &NamedType) -> TokenStream2 {
|
||||||
let ident = identifier_to_rust(&named_type.name);
|
let ident = identifier_to_rust(&named_type.name);
|
||||||
quote! {
|
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 convert_case::{Case, Casing};
|
||||||
use proc_macro2::{Ident, TokenStream as TokenStream2};
|
use proc_macro2::{Ident, TokenStream as TokenStream2};
|
||||||
use quote::{format_ident, quote};
|
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;
|
use super::identifier_to_rust;
|
||||||
|
|
||||||
|
@ -57,9 +57,28 @@ pub fn type_to_c(r#type: &Type, is_output: bool) -> TokenStream2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn identifier_to_c(identifier: &Identifier) -> TokenStream2 {
|
pub fn identifier_to_c(identifier: &Identifier) -> TokenStream2 {
|
||||||
let ident = format_ident!("{}_t", identifier.name.to_case(Case::Snake));
|
match identifier.variant {
|
||||||
quote! {
|
Variant::Structure => {
|
||||||
#ident
|
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 {
|
pub fn type_to_c_equalivalent(r#type: &Type, namespaces: &[&Identifier]) -> TokenStream2 {
|
||||||
|
|
|
@ -253,7 +253,7 @@ fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#debug
|
#debug
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Convertible, TypeInfo)]
|
#[derive(Convertible)]
|
||||||
pub enum #ident {
|
pub enum #ident {
|
||||||
#(#states),*
|
#(#states),*
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ fn structure_to_rust(structure: &Structure) -> TokenStream2 {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#debug
|
#debug
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Convertible, TypeInfo)]
|
#[derive(Convertible)]
|
||||||
pub struct #ident {
|
pub struct #ident {
|
||||||
#(#contents),*
|
#(#contents),*
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,29 @@ use crate::lexing::TokenKind;
|
||||||
|
|
||||||
use super::unchecked;
|
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)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Namespace {
|
pub struct Namespace {
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
|
@ -128,16 +151,20 @@ impl From<&DocNamedType> for NamedType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TokenKind> for Identifier {
|
impl TokenKind {
|
||||||
fn from(value: TokenKind) -> Self {
|
pub fn to_identifier(self: TokenKind, variant: Variant) -> Identifier {
|
||||||
match value {
|
match self {
|
||||||
TokenKind::Identifier(ident) => Identifier { name: ident },
|
TokenKind::Identifier(ident) => Identifier {
|
||||||
|
name: ident,
|
||||||
|
variant,
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!(
|
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)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
pub struct Identifier {
|
pub struct Identifier {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub variant: Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
pub struct DocIdentifier {
|
pub struct DocIdentifier {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub variant: Variant,
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +207,7 @@ impl From<&DocIdentifier> for Identifier {
|
||||||
fn from(value: &DocIdentifier) -> Self {
|
fn from(value: &DocIdentifier) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: value.name.to_owned(),
|
name: value.name.to_owned(),
|
||||||
|
variant: value.variant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ use crate::{
|
||||||
NamedType as UncheckedNamedType, Namespace as UncheckedNamespace,
|
NamedType as UncheckedNamedType, Namespace as UncheckedNamespace,
|
||||||
Structure as UncheckedStructure, Type as UncheckedType,
|
Structure as UncheckedStructure, Type as UncheckedType,
|
||||||
},
|
},
|
||||||
|
Variant,
|
||||||
},
|
},
|
||||||
error::ErrorContext,
|
error::ErrorContext,
|
||||||
lexing::{TokenKind, TokenSpan},
|
lexing::{TokenKind, TokenSpan},
|
||||||
|
@ -92,10 +93,14 @@ impl Parser {
|
||||||
) -> Result<Namespace, ParsingError> {
|
) -> Result<Namespace, ParsingError> {
|
||||||
let namespace_span = namespace.name.span;
|
let namespace_span = namespace.name.span;
|
||||||
let name = match namespace.name.kind {
|
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
|
// This is not really used, so the value put here does not matter
|
||||||
TokenKind::Dummy => Identifier {
|
TokenKind::Dummy => Identifier {
|
||||||
name: "".to_owned(),
|
name: "".to_owned(),
|
||||||
|
variant: Variant::Void,
|
||||||
},
|
},
|
||||||
_ => unreachable!("This should never be more than these two enum veriants"),
|
_ => unreachable!("This should never be more than these two enum veriants"),
|
||||||
};
|
};
|
||||||
|
@ -144,7 +149,7 @@ impl Parser {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut function: UncheckedFunction,
|
mut function: UncheckedFunction,
|
||||||
) -> Result<Function, ParsingError> {
|
) -> 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![];
|
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)?);
|
||||||
|
@ -172,7 +177,8 @@ impl Parser {
|
||||||
self.enumerations.push(enumeration.clone());
|
self.enumerations.push(enumeration.clone());
|
||||||
|
|
||||||
let enum_span = enumeration.identifier.span;
|
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 {
|
if &identifier == namespace_name {
|
||||||
return Err(ParsingError::EnumWithNamespaceName {
|
return Err(ParsingError::EnumWithNamespaceName {
|
||||||
name: identifier.clone(),
|
name: identifier.clone(),
|
||||||
|
@ -191,10 +197,12 @@ impl Parser {
|
||||||
let mut states = vec![];
|
let mut states = vec![];
|
||||||
for mut state in enumeration.states {
|
for mut state in enumeration.states {
|
||||||
states.push({
|
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 {
|
DocIdentifier {
|
||||||
name: ident.name,
|
name: ident.name,
|
||||||
attributes: pass_attrs_along!(state),
|
attributes: pass_attrs_along!(state),
|
||||||
|
variant: Variant::DocNamedType,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -212,7 +220,8 @@ impl Parser {
|
||||||
) -> Result<Structure, ParsingError> {
|
) -> Result<Structure, ParsingError> {
|
||||||
self.structures.push(structure.clone());
|
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![];
|
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)?);
|
||||||
|
@ -229,7 +238,8 @@ impl Parser {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut named_type: UncheckedNamedType,
|
mut named_type: UncheckedNamedType,
|
||||||
) -> Result<NamedType, ParsingError> {
|
) -> 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)?;
|
let r#type: Type = self.process_type(named_type.r#type)?;
|
||||||
Ok(NamedType { name, r#type })
|
Ok(NamedType { name, r#type })
|
||||||
}
|
}
|
||||||
|
@ -237,7 +247,8 @@ impl Parser {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut doc_named_type: UncheckedDocNamedType,
|
mut doc_named_type: UncheckedDocNamedType,
|
||||||
) -> Result<DocNamedType, ParsingError> {
|
) -> 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)?;
|
let r#type: Type = self.process_type(doc_named_type.r#type)?;
|
||||||
Ok(DocNamedType {
|
Ok(DocNamedType {
|
||||||
name,
|
name,
|
||||||
|
@ -247,27 +258,40 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_type(&mut self, mut r#type: UncheckedType) -> Result<Type, ParsingError> {
|
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
|
.structures
|
||||||
.iter()
|
.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)
|
.any(|ident| ident == identifier)
|
||||||
&& !self
|
|
||||||
.enumerations
|
|
||||||
.iter()
|
|
||||||
.map(|r#enum| Into::<Identifier>::into(r#enum.identifier.kind.clone()))
|
|
||||||
.any(|ident| ident == identifier)
|
|
||||||
&& !BASE_TYPES
|
|
||||||
.iter()
|
|
||||||
.any(|(ident, _)| ident == &identifier.name)
|
|
||||||
{
|
{
|
||||||
|
variant = Variant::Structure;
|
||||||
|
} else if self
|
||||||
|
.enumerations
|
||||||
|
.iter()
|
||||||
|
.map(|r#enum| (r#enum.identifier.kind.clone()).to_identifier(Variant::Void))
|
||||||
|
.any(|ident| ident == identifier)
|
||||||
|
{
|
||||||
|
variant = Variant::Enumeration;
|
||||||
|
} else if BASE_TYPES
|
||||||
|
.iter()
|
||||||
|
.any(|(ident, _)| ident == &identifier.name)
|
||||||
|
{
|
||||||
|
variant = Variant::Primitive;
|
||||||
|
} else {
|
||||||
return Err(ParsingError::TypeNotDeclared {
|
return Err(ParsingError::TypeNotDeclared {
|
||||||
r#type: identifier,
|
r#type: identifier,
|
||||||
span: r#type.identifier.span,
|
span: r#type.identifier.span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let identifier: Identifier = Identifier {
|
||||||
|
name: identifier.name,
|
||||||
|
variant,
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let fitting_types: Vec<&usize> = BASE_TYPES
|
let fitting_types: Vec<&usize> = BASE_TYPES
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -22,6 +22,7 @@ use crate::command_spec::checked::{
|
||||||
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
||||||
NamedType, Namespace, Structure, Type,
|
NamedType, Namespace, Structure, Type,
|
||||||
};
|
};
|
||||||
|
use crate::command_spec::Variant;
|
||||||
use crate::lexing::TokenStream;
|
use crate::lexing::TokenStream;
|
||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
@ -59,19 +60,23 @@ mod trinitrix {
|
||||||
namespaces: vec![Namespace {
|
namespaces: vec![Namespace {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "trinitrix".to_owned(),
|
name: "trinitrix".to_owned(),
|
||||||
|
variant: Variant::Namespace,
|
||||||
},
|
},
|
||||||
functions: vec![Function {
|
functions: vec![Function {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "execute_callback".to_owned(),
|
name: "execute_callback".to_owned(),
|
||||||
|
variant: Variant::Function,
|
||||||
},
|
},
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NamedType {
|
NamedType {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "callback".to_owned(),
|
name: "callback".to_owned(),
|
||||||
|
variant: Variant::NamedType,
|
||||||
},
|
},
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "Callback".to_owned(),
|
name: "Callback".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
},
|
},
|
||||||
|
@ -79,10 +84,12 @@ mod trinitrix {
|
||||||
NamedType {
|
NamedType {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "priority".to_owned(),
|
name: "priority".to_owned(),
|
||||||
|
variant: Variant::NamedType,
|
||||||
},
|
},
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "CallbackPriority".to_owned(),
|
name: "CallbackPriority".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
},
|
},
|
||||||
|
@ -94,15 +101,18 @@ mod trinitrix {
|
||||||
structures: vec![Structure {
|
structures: vec![Structure {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "Callback".to_owned(),
|
name: "Callback".to_owned(),
|
||||||
|
variant: Variant::Structure,
|
||||||
},
|
},
|
||||||
contents: vec![
|
contents: vec![
|
||||||
DocNamedType {
|
DocNamedType {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "func".to_owned(),
|
name: "func".to_owned(),
|
||||||
|
variant: Variant::DocNamedType,
|
||||||
},
|
},
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "()".to_owned(),
|
name: "()".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
},
|
},
|
||||||
|
@ -111,10 +121,12 @@ mod trinitrix {
|
||||||
DocNamedType {
|
DocNamedType {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "timeout".to_owned(),
|
name: "timeout".to_owned(),
|
||||||
|
variant: Variant::DocNamedType,
|
||||||
},
|
},
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "u8".to_owned(),
|
name: "u8".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
},
|
},
|
||||||
|
@ -126,19 +138,23 @@ mod trinitrix {
|
||||||
enumerations: vec![Enumeration {
|
enumerations: vec![Enumeration {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "CallbackPriority".to_owned(),
|
name: "CallbackPriority".to_owned(),
|
||||||
|
variant: Variant::Enumeration,
|
||||||
},
|
},
|
||||||
states: vec![
|
states: vec![
|
||||||
DocIdentifier {
|
DocIdentifier {
|
||||||
name: "High".to_owned(),
|
name: "High".to_owned(),
|
||||||
attributes: vec![],
|
attributes: vec![],
|
||||||
|
variant: Variant::DocNamedType,
|
||||||
},
|
},
|
||||||
DocIdentifier {
|
DocIdentifier {
|
||||||
name: "Medium".to_owned(),
|
name: "Medium".to_owned(),
|
||||||
attributes: vec![],
|
attributes: vec![],
|
||||||
|
variant: Variant::DocNamedType,
|
||||||
},
|
},
|
||||||
DocIdentifier {
|
DocIdentifier {
|
||||||
name: "Low".to_owned(),
|
name: "Low".to_owned(),
|
||||||
attributes: vec![],
|
attributes: vec![],
|
||||||
|
variant: Variant::DocNamedType,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
attributes: vec![],
|
attributes: vec![],
|
||||||
|
@ -171,7 +187,8 @@ fn execute_callback(callback: Name);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
r#type,
|
r#type,
|
||||||
Identifier {
|
Identifier {
|
||||||
name: "Name".to_owned()
|
name: "Name".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -202,14 +219,17 @@ mod trinitrix {
|
||||||
functions: vec![Function {
|
functions: vec![Function {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "print".to_owned(),
|
name: "print".to_owned(),
|
||||||
|
variant: Variant::Function,
|
||||||
},
|
},
|
||||||
inputs: vec![NamedType {
|
inputs: vec![NamedType {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "message".to_owned(),
|
name: "message".to_owned(),
|
||||||
|
variant: Variant::NamedType,
|
||||||
},
|
},
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "String".to_owned(),
|
name: "String".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
},
|
},
|
||||||
|
@ -220,18 +240,22 @@ mod trinitrix {
|
||||||
namespaces: vec![Namespace {
|
namespaces: vec![Namespace {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "trinitrix".to_owned(),
|
name: "trinitrix".to_owned(),
|
||||||
|
variant: Variant::Namespace,
|
||||||
},
|
},
|
||||||
functions: vec![Function {
|
functions: vec![Function {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "hi".to_owned(),
|
name: "hi".to_owned(),
|
||||||
|
variant: Variant::Function,
|
||||||
},
|
},
|
||||||
inputs: vec![NamedType {
|
inputs: vec![NamedType {
|
||||||
name: Identifier {
|
name: Identifier {
|
||||||
name: "name".to_owned(),
|
name: "name".to_owned(),
|
||||||
|
variant: Variant::NamedType,
|
||||||
},
|
},
|
||||||
r#type: Type {
|
r#type: Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "String".to_owned(),
|
name: "String".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
},
|
},
|
||||||
|
@ -239,6 +263,7 @@ mod trinitrix {
|
||||||
output: Some(Type {
|
output: Some(Type {
|
||||||
identifier: Identifier {
|
identifier: Identifier {
|
||||||
name: "String".to_owned(),
|
name: "String".to_owned(),
|
||||||
|
variant: Variant::Type,
|
||||||
},
|
},
|
||||||
generic_args: vec![],
|
generic_args: vec![],
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -30,3 +30,4 @@ 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"}
|
||||||
|
|
|
@ -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")]
|
#[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 },
|
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!")]
|
#[error("You passed a null pointer to the conversion function!")]
|
||||||
NullPointer,
|
NullPointer,
|
||||||
|
|
||||||
|
@ -38,4 +41,7 @@ pub enum TypeConversionError {
|
||||||
|
|
||||||
#[error("The returned Result was an error: {original_message}")]
|
#[error("The returned Result was an error: {original_message}")]
|
||||||
ResultWasErr { original_message: String },
|
ResultWasErr { original_message: String },
|
||||||
|
|
||||||
|
#[error("This funciton is not admissable")]
|
||||||
|
NotAdmissable,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::error::{self, TypeConversionError};
|
use crate::error::{self, TypeConversionError};
|
||||||
|
use log::warn;
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
ffi::{c_char, CString},
|
ffi::{c_char, CString},
|
||||||
|
@ -8,14 +9,24 @@ use std::{
|
||||||
|
|
||||||
use super::errno;
|
use super::errno;
|
||||||
|
|
||||||
|
pub use trixy_types_derive::Convertible;
|
||||||
|
|
||||||
/// Convert a value into a data structure that we can send directly to c.
|
/// 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;
|
type Ptr;
|
||||||
/// Turn the value into a c void pointer or a data structure that c can understand.
|
/// Turn the value into a c void pointer or a data structure that c can understand.
|
||||||
fn into_ptr(self) -> Self::Ptr;
|
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.
|
/// 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 {
|
macro_rules! primitive_convert {
|
||||||
|
@ -27,8 +38,9 @@ macro_rules! primitive_convert {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
fn from_ptr(_: Self::Ptr) -> Result<$name, error::TypeConversionError> {
|
||||||
unreachable!("Theres is no need to drop a {}", stringify!($name))
|
warn!("Dropping a {}; Theres is no need to drop a {}", stringify!($name), stringify!($name));
|
||||||
|
Ok(<$name>::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,35 +63,52 @@ primitive_convert!(f32);
|
||||||
primitive_convert!(f64);
|
primitive_convert!(f64);
|
||||||
|
|
||||||
impl Convertible for CString {
|
impl Convertible for CString {
|
||||||
type Ptr = *const *const c_char;
|
type Ptr = *const c_char;
|
||||||
|
|
||||||
fn into_ptr(self) -> Self::Ptr {
|
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> {
|
fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
|
||||||
unsafe {
|
if ptr.is_null() {
|
||||||
if ptr.is_null() {
|
return Err(TypeConversionError::NullPointer);
|
||||||
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(())
|
// 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 = unsafe { CString::from_raw(ptr as *mut i8) };
|
||||||
|
Ok(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
impl<T: Convertible> Convertible for Option<T> {
|
||||||
type Ptr = *const T;
|
type Ptr = *const T;
|
||||||
|
|
||||||
|
@ -92,14 +121,15 @@ impl<T: Convertible> Convertible for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
|
||||||
unreachable!(
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,14 +151,16 @@ impl<T: Convertible, E: Error> Convertible for Result<T, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
|
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
|
||||||
unreachable!(
|
warn!(
|
||||||
"
|
"
|
||||||
This should never be called, as this result type resolves
|
This should never be called, as this result type resolves
|
||||||
to a null pointer (and a set error) or to a pointer to
|
to a null pointer (and a set error) or to a pointer to
|
||||||
the real value (e. g. a `crate::Vec<T>`)
|
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> {
|
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 {
|
let vec = unsafe {
|
||||||
if ptr.data.is_null() {
|
|
||||||
return Err(TypeConversionError::NullPointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(@soispha): Add this, when this feature is stable <2024-02-17>
|
|
||||||
// if !ptr.data.is_aligned() {
|
|
||||||
// return Err(TypeConversionError::NotAligned);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// We simply hope that c treated our vector as read-only and didn't modify it.
|
// 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.
|
// See the SAFETY section of the String implementation for more detail.
|
||||||
Vec::from_raw_parts(ptr.data as *mut T, ptr.length, ptr.capacity)
|
Vec::from_raw_parts(ptr.data as *mut T, ptr.length, ptr.capacity)
|
||||||
};
|
};
|
||||||
drop(vec);
|
Ok(vec)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
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
|
// This code is heavily inspired by: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static LAST_ERROR: RefCell<Option<Box<TypeConversionError>>> = RefCell::new(None);
|
static LAST_ERROR: RefCell<Option<Box<TypeConversionError>>> = RefCell::new(None);
|
||||||
|
|
|
@ -20,3 +20,5 @@
|
||||||
|
|
||||||
pub mod convert_trait;
|
pub mod convert_trait;
|
||||||
pub mod errno;
|
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
|
// 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;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct String(pub(crate) *const c_char);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Vec<T> {
|
pub struct Vec<T> {
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
# build
|
# build
|
||||||
/target
|
/target
|
||||||
/result
|
/result
|
||||||
/dist
|
|
||||||
|
|
||||||
# C stuff
|
|
||||||
*.d
|
|
||||||
*.o
|
|
||||||
|
|
||||||
# This crate is a library
|
# This crate is a library
|
||||||
Cargo.lock
|
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