From e968e03743eb12e66eb3109ea0a58a6d6310518b Mon Sep 17 00:00:00 2001 From: dehann Date: Tue, 14 Jan 2025 07:38:18 -0800 Subject: [PATCH 1/6] clib example --- .github/workflows/ci.yml | 8 ++-- .gitignore | 1 + Cargo.lock | 64 ++++++++++++++++++++---------- Cargo.toml | 15 +++---- Makefile | 17 ++++++++ include/NavAbilitySDK.h | 26 +++++++++++++ src/lib.rs | 84 ++++++++++++++++++++++++++++++++++++++++ test/Makefile | 11 ++++++ test/test.c | 62 +++++++++++++++++++++++++++++ 9 files changed, 257 insertions(+), 31 deletions(-) create mode 100644 Makefile create mode 100644 include/NavAbilitySDK.h create mode 100644 test/Makefile create mode 100644 test/test.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2165b8..727b14e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,12 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build wasm - run: cargo build -F wasm + run: make build-wasm - name: Build tokio - run: cargo build -F tokio + run: make build-tokio - name: Run tests - run: cargo test -F tokio + run: | + make test-tokio + make test-capi env: NAVABILITY_USERLABEL: "test@wherewhen.ai" diff --git a/.gitignore b/.gitignore index ea8c4bf..d4bf468 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/test/build \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b22dd57..ed5ca46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -684,9 +684,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "linux-raw-sys" @@ -696,9 +696,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" @@ -758,7 +758,7 @@ dependencies = [ "gloo-console", "graphql_client", "log", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "tokio", @@ -918,9 +918,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -951,6 +951,7 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", + "tower", "tower-service", "url", "wasm-bindgen", @@ -1081,18 +1082,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1101,9 +1102,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -1294,9 +1295,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -1341,11 +1342,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.1", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -1433,9 +1455,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" dependencies = [ "getrandom", "sha1_smol", diff --git a/Cargo.toml b/Cargo.toml index c571965..cca97aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,14 +8,14 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -serde = "^1.0.216" -serde_json = "^1.0.133" +serde = "^1.0.217" +serde_json = "^1.0.135" chrono = "^0.4.39" -log = "^0.4" -uuid = { version ="^1.11", features = ["v4","v5"] } +log = "^0.4.25" +uuid = { version ="^1.11.1", features = ["v4","v5"] } tracing = "^0.1.41" graphql_client = "^0.14" -reqwest = {version = "^0.12.9", optional=true, features = [ +reqwest = {version = "^0.12.12", optional=true, features = [ "json", # "multipart", ] } @@ -24,7 +24,8 @@ reqwest = {version = "^0.12.9", optional=true, features = [ [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { version = "^1.40", default-features = false, optional=true} +tokio = { version = "^1.43", default-features = false, optional=true} +# libc = { version = "^0.2.85", default-features = false, optional=true} [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -32,7 +33,7 @@ gloo-console = { version = "^0.3", optional = true } [features] -tokio = ["dep:tokio", "dep:reqwest"] +tokio = ["dep:tokio", "dep:reqwest"] #, "dep:libc"] blocking = ["graphql_client/reqwest","graphql_client/reqwest-blocking"] wasm = ["dep:reqwest"] wasm-dev = ["dep:gloo-console"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4a3140f --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ + +clean: + cargo clean + +build-wasm: + cargo build -F wasm + +build-tokio: + cargo build -F tokio + +build-lib: build-tokio + +test-tokio: + cargo test -F tokio + +test-capi: build-lib + cd test && $(MAKE) \ No newline at end of file diff --git a/include/NavAbilitySDK.h b/include/NavAbilitySDK.h new file mode 100644 index 0000000..c8507f5 --- /dev/null +++ b/include/NavAbilitySDK.h @@ -0,0 +1,26 @@ +#ifndef NAVABILITYSDK +#define NAVABILITYSDK + +// File: NavAbilitySDK.h + +// Lets use some types which we can easily pair with rust types. +#include + + +// Opaque type for some Struct +typedef struct _nvaclient NavAbilityClient; + + +NavAbilityClient* NvaClient(void); +void free_NvaClient(NavAbilityClient *n); + +char* get_apiurl(const NavAbilityClient *n); + + +// typedef struct _custom_error TestType; +// // Free function which takes a pointer to a pointer for freeing +// // the memory, returns error code based on ERRNO. +// int32_t error_free_with_result(TestType **o); + + +#endif \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ef21984..d03c607 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1536,6 +1536,90 @@ fn to_console_error( +use std::ffi::{ + CString, + CStr +}; +use std::os::raw::c_char; + + +pub unsafe fn convert_str(input: &str) -> *mut c_char { + let c_str = CString::new(input).unwrap().into_raw(); + return c_str; +} + +pub unsafe fn cstr_to_str(c_buf: *const i8) -> &'static str { + let cstr = CStr::from_ptr(c_buf); + return cstr.to_str().expect("success"); +} + + +// ref. https://doc.rust-lang.org/std/boxed/ +#[no_mangle] +pub unsafe extern "C" fn NvaClient( +) -> Box { + return Box::new(NavAbilityClient::new( + &"https://api.navability.io/graphql".to_owned(), + &"".to_owned(), + &"".to_owned(), + )) +} + +impl Drop for NavAbilityClient { + fn drop(&mut self) { + println!("Dropping NavAbilityClient."); + } +} + +// Take ownership via passing by value, i.e. runs drop on fn exit. Option for null case. +#[no_mangle] +pub extern "C" fn free_NvaClient( + _: Option> +) {} + +#[no_mangle] +pub unsafe extern "C" fn get_apiurl(n: &NavAbilityClient) -> *mut c_char { + return convert_str(&n.apiurl) +} + +#[no_mangle] +pub unsafe extern "C" fn drop_cstr2(pointer: *mut c_char) -> () { + drop(CString::from_raw(pointer)); +} + + +// pub struct TestType { +// code: isize, +// } +// #[no_mangle] +// pub unsafe extern "C" fn error_create_with_result(_e: *mut *mut TestType) -> isize { +// let e = Box::new(example_error()); +// *_e = Box::into_raw(e); +// 0 +// } +// #[no_mangle] +// pub unsafe extern "C" fn error_free_with_result(e: *mut *mut TestType) -> i32 { +// if e.is_null() || (*e).is_null() { +// return libc::EINVAL; +// } +// // Reconstruct the Error into a box and then drop it so that it's freed. +// drop(Box::from_raw(*e)); +// *e = 0 as *mut TestType; +// 0 +// } +// // Our example "getter" methods which work on the Error type. The value +// // returned is only valid as long as the Error has not been freed. If C +// // caller needs a longer lifetime they need to copy the value. +// #[no_mangle] +// pub extern "C" fn error_code_get(e: &TestType) -> isize { +// e.code +// } +// use libc; + + + + + diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..fce0563 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,11 @@ + +.DEFAULT_GOAL := run-testc + +build-testc: test.c + mkdir -p build + gcc -Wall -g -O0 test.c -o build/a.out -I../include/ -L../target/debug/ -lnavabilitysdk +.PHONY: build-testc + +run-testc: build-testc + LD_LIBRARY_PATH=../target/debug ./build/a.out +.PHONY: run-testc diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..4b0988d --- /dev/null +++ b/test/test.c @@ -0,0 +1,62 @@ +#include +#include +#include "NavAbilitySDK.h" +#include + +// File: main.c +// +// Sample library usage. +int main(void) { + + NavAbilityClient* nvacl = NULL; + nvacl = NvaClient(); + + char* api = get_apiurl(nvacl); + + printf("nvacl.apiurl = %s\n", api); + + // drop_cstr2(api); + free_NvaClient(nvacl); + + + return 0; +} + + // Error2 *e = NULL; + // int result = 0; + + // char *s = NULL; + + // result = get_some_cstr_2(&s); + // if (0 == result) { + // //printf("get_some_cstr_2 returned %s\n", s); + // free(s); + // s = NULL; + // } else { + // printf("get_some_cstr_2 Result = %d\n", result); + // return 10; + // } + + // e = error_new(); + // const char *msg = error_msg_get(e); + // if (msg) { + // printf("error message = %s\n", msg); + // printf("error code = %d\n", error_code_get(e)); + // } else { + // printf("Error msg is null :-/\n"); + // return 1; + // } + + // error_free(e); + + // e = NULL; + // result = error_create_with_result(&e); + // if (result == 0) { + // printf("error message = %s\n", error_msg_get(e)); + // printf("error code = %d\n", error_code_get(e)); + + // printf("Result of freeing %d\n", error_free_with_result(&e)); + // printf("Value of e = %p (expecting nil)\n", e); + // } else { + // printf("Error: error_create_with_result = %d\n", result); + // } \ No newline at end of file From 70e3ab95b27760c1761b64febff5d4a578463ded Mon Sep 17 00:00:00 2001 From: dehann Date: Thu, 16 Jan 2025 01:55:31 -0800 Subject: [PATCH 2/6] clib test w return vec val --- .github/workflows/ci.yml | 4 +- Cargo.lock | 32 ++++++- Cargo.toml | 7 +- Makefile | 8 +- cbindgen.toml | 159 ++++++++++++++++++++++++++++++++ include/NavAbilitySDK.h | 37 ++++---- include/NavAbilitySDK.hpp | 31 +++++++ src/lib.rs | 187 ++++++++++++++++++++++++++++++++------ test/test.c | 13 ++- 9 files changed, 425 insertions(+), 53 deletions(-) create mode 100644 cbindgen.toml create mode 100644 include/NavAbilitySDK.hpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 727b14e..6f3f551 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: - name: Run tests run: | make test-tokio - make test-capi + make generate-cbindgen-c + make generate-cbindgen-cpp + # make test-capi env: NAVABILITY_USERLABEL: "test@wherewhen.ai" diff --git a/Cargo.lock b/Cargo.lock index ed5ca46..42ec981 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,6 +199,28 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "ffi-convert" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83979d1b24808a399d1506a7213c3f3c512fa1d8701a27e80141e47ff09aac93" +dependencies = [ + "ffi-convert-derive", + "libc", + "thiserror", +] + +[[package]] +name = "ffi-convert-derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9ea801ee0e90ec33e6483df59c4644b89c49be7cd0e50b2a762773e552afba" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "fnv" version = "1.0.7" @@ -419,9 +441,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -647,9 +669,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -755,8 +777,10 @@ name = "navabilitysdk" version = "0.1.0" dependencies = [ "chrono", + "ffi-convert", "gloo-console", "graphql_client", + "libc", "log", "reqwest 0.12.12", "serde", diff --git a/Cargo.toml b/Cargo.toml index cca97aa..3ccb75f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ serde_json = "^1.0.135" chrono = "^0.4.39" log = "^0.4.25" uuid = { version ="^1.11.1", features = ["v4","v5"] } -tracing = "^0.1.41" graphql_client = "^0.14" reqwest = {version = "^0.12.12", optional=true, features = [ "json", @@ -25,7 +24,9 @@ reqwest = {version = "^0.12.12", optional=true, features = [ [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { version = "^1.43", default-features = false, optional=true} -# libc = { version = "^0.2.85", default-features = false, optional=true} +tracing = "^0.1.41" +ffi-convert = { version="^0.6.1", optional=true } +libc = { version = "^0.2.85", default-features = false, optional=true} [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -33,7 +34,7 @@ gloo-console = { version = "^0.3", optional = true } [features] -tokio = ["dep:tokio", "dep:reqwest"] #, "dep:libc"] +tokio = ["dep:tokio", "dep:reqwest", "dep:ffi-convert", "dep:libc"] blocking = ["graphql_client/reqwest","graphql_client/reqwest-blocking"] wasm = ["dep:reqwest"] wasm-dev = ["dep:gloo-console"] diff --git a/Makefile b/Makefile index 4a3140f..70a1339 100644 --- a/Makefile +++ b/Makefile @@ -14,4 +14,10 @@ test-tokio: cargo test -F tokio test-capi: build-lib - cd test && $(MAKE) \ No newline at end of file + cd test && $(MAKE) + +generate-cbinden-cpp: + cbindgen --config cbindgen.toml --crate navabilitysdk --output include/NavAbilitySDK.hpp + +generate-cbinden-c: + cbindgen --config cbindgen.toml --lang c --crate navabilitysdk --output include/NavAbilitySDK.h \ No newline at end of file diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000..8f62a13 --- /dev/null +++ b/cbindgen.toml @@ -0,0 +1,159 @@ +# This is a template cbindgen.toml file with all of the default values. +# Some values are commented out because their absence is the real default. +# +# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml +# for detailed documentation of every option here. + + + +language = "C" + + + +############## Options for Wrapping the Contents of the Header ################# + +# header = "/* Text to put at the beginning of the generated file. Probably a license. */" +# trailer = "/* Text to put at the end of the generated file */" +# include_guard = "my_bindings_h" +# pragma_once = true +# autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" +include_version = false +# namespace = "my_namespace" +namespaces = [] +using_namespaces = [] +sys_includes = [] +includes = [] +no_includes = false +# cpp_compat = true +after_includes = "" + + + + +############################ Code Style Options ################################ + +braces = "SameLine" +line_length = 100 +tab_width = 2 +documentation = true +documentation_style = "auto" +documentation_length = "full" +line_endings = "LF" # also "CR", "CRLF", "Native" + + + + +############################# Codegen Options ################################## + +style = "both" +sort_by = "Name" # default for `fn.sort_by` and `const.sort_by` +usize_is_size_t = true + + + +[defines] +# "target_os = freebsd" = "DEFINE_FREEBSD" +# "feature = serde" = "DEFINE_SERDE" + + + +[export] +include = [] +exclude = [] +# prefix = "CAPI_" +item_types = [] +renaming_overrides_prefixing = false + + + +[export.rename] + + + +[export.body] + + +[export.mangle] + + +[fn] +rename_args = "None" +# must_use = "MUST_USE_FUNC" +# deprecated = "DEPRECATED_FUNC" +# deprecated_with_note = "DEPRECATED_FUNC_WITH_NOTE" +# no_return = "NO_RETURN" +# prefix = "START_FUNC" +# postfix = "END_FUNC" +args = "auto" +sort_by = "Name" + + + + +[struct] +rename_fields = "None" +# must_use = "MUST_USE_STRUCT" +# deprecated = "DEPRECATED_STRUCT" +# deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE" +derive_constructor = false +derive_eq = false +derive_neq = false +derive_lt = false +derive_lte = false +derive_gt = false +derive_gte = false + + + + +[enum] +rename_variants = "None" +# must_use = "MUST_USE_ENUM" +# deprecated = "DEPRECATED_ENUM" +# deprecated_with_note = "DEPRECATED_ENUM_WITH_NOTE" +add_sentinel = false +prefix_with_name = false +derive_helper_methods = false +derive_const_casts = false +derive_mut_casts = false +# cast_assert_name = "ASSERT" +derive_tagged_enum_destructor = false +derive_tagged_enum_copy_constructor = false +enum_class = true +private_default_tagged_enum_constructor = false + + + + +[const] +allow_static_const = true +allow_constexpr = false +sort_by = "Name" + + + + +[macro_expansion] +bitflags = false + + + + + + +############## Options for How Your Rust library Should Be Parsed ############## + +[parse] +parse_deps = false +# include = [] +exclude = [] +clean = false +extra_bindings = [] + + + +[parse.expand] +crates = [] +all_features = false +default_features = true +features = [] diff --git a/include/NavAbilitySDK.h b/include/NavAbilitySDK.h index c8507f5..090200c 100644 --- a/include/NavAbilitySDK.h +++ b/include/NavAbilitySDK.h @@ -1,26 +1,31 @@ -#ifndef NAVABILITYSDK -#define NAVABILITYSDK - -// File: NavAbilitySDK.h - -// Lets use some types which we can easily pair with rust types. +#include +#include +#include #include +#include + +typedef struct NavAbilityClient NavAbilityClient; -// Opaque type for some Struct -typedef struct _nvaclient NavAbilityClient; +typedef struct CAgent { + const char *id; + const char *label; + const char *created_timestamp; +} CAgent; +typedef struct RVec_CAgent { + struct CAgent *ptr; + size_t len; +} RVec_CAgent; -NavAbilityClient* NvaClient(void); -void free_NvaClient(NavAbilityClient *n); +struct NavAbilityClient *NvaClient(const char *orgid, const char *api_token); -char* get_apiurl(const NavAbilityClient *n); +void drop_cstr(char *pointer); +void free_NvaClient(struct NavAbilityClient*); -// typedef struct _custom_error TestType; -// // Free function which takes a pointer to a pointer for freeing -// // the memory, returns error code based on ERRNO. -// int32_t error_free_with_result(TestType **o); +void free_rvec_cagent(struct RVec_CAgent rvec); +char *get_apiurl(const struct NavAbilityClient *n); -#endif \ No newline at end of file +struct RVec_CAgent list_agents(struct NavAbilityClient *_nvacl); diff --git a/include/NavAbilitySDK.hpp b/include/NavAbilitySDK.hpp new file mode 100644 index 0000000..090200c --- /dev/null +++ b/include/NavAbilitySDK.hpp @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + + +typedef struct NavAbilityClient NavAbilityClient; + +typedef struct CAgent { + const char *id; + const char *label; + const char *created_timestamp; +} CAgent; + +typedef struct RVec_CAgent { + struct CAgent *ptr; + size_t len; +} RVec_CAgent; + +struct NavAbilityClient *NvaClient(const char *orgid, const char *api_token); + +void drop_cstr(char *pointer); + +void free_NvaClient(struct NavAbilityClient*); + +void free_rvec_cagent(struct RVec_CAgent rvec); + +char *get_apiurl(const struct NavAbilityClient *n); + +struct RVec_CAgent list_agents(struct NavAbilityClient *_nvacl); diff --git a/src/lib.rs b/src/lib.rs index d03c607..fb464c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1015,6 +1015,25 @@ pub async fn send_blob_entry( } + +#[cfg(target_arch = "wasm32")] +pub async fn create_upload_web( + send_into: Sender, + client: &NavAbilityClient, + name: &String, + blob_size: i64, + nparts: Option, + blob_id: Option, // doenst work yet, leave None +) { + let result = create_upload_async( + client.clone(), + blob_id.expect("Must provide blob_id to create_upload_web"), + nparts, + ).await; + send_query_result(send_into, result); +} + + #[cfg(target_arch = "wasm32")] pub async fn fetch_context_web( send_into: Sender>, @@ -1093,23 +1112,6 @@ pub async fn fetch_ur_list_web( } -#[cfg(target_arch = "wasm32")] -pub async fn create_upload_web( - send_into: Sender, - client: &NavAbilityClient, - name: &String, - blob_size: i64, - nparts: Option, - blob_id: Option, // doenst work yet, leave None -) { - let result = create_upload_async( - client.clone(), - blob_id.expect("Must provide blob_id to create_upload_web"), - nparts, - ).await; - send_query_result(send_into, result); -} - // #[cfg(not(target_arch = "wasm32"))] @@ -1125,18 +1127,25 @@ pub fn fetch_ur_list_tokio( .unwrap(); let ur_list_data = rt.block_on(async { fetch_robots_async(&nvacl).await - }).unwrap().data; + }); //.unwrap().data; + match ur_list_data { // .data.expect("Problem with GQL response") + Ok(ur_data) => match ur_data.data { Some(data) => { let ur_list = data.agents; if let Err(e) = send_into.send(ur_list) { - tracing::error!("Error sending user robot list data: {}", e); + to_console_error(&format!("Error sending user robot list data: {:?}", e)); }; return Ok(()) }, None => { - return panic!("Problem with GQL response"); + to_console_error(&format!("GQL errors {:?}\n",ur_data.errors)); + return panic!("Problem with GQL response"); + } + } + Err(e) => { + return panic!("Something went wrong"); } } } @@ -1382,7 +1391,8 @@ fn to_console_debug( text: &str ) { #[cfg(not(target_arch = "wasm32"))] - tracing::debug!("{}",text); + println!("{}",text); + // tracing::debug!("{}",text); #[cfg(target_arch = "wasm32")] gloo_console::log!(text.to_string()); } @@ -1391,7 +1401,8 @@ fn to_console_error( text: &str ) { #[cfg(not(target_arch = "wasm32"))] - tracing::error!("ERROR NvaSDK.rs {}",&text); + println!("ERROR NvaSDK.rs {}",&text); + // tracing::error!("ERROR NvaSDK.rs {}",&text); #[cfg(target_arch = "wasm32")] gloo_console::log!(&format!("ERROR NvaSDK.rs {}",&text)); } @@ -1557,33 +1568,157 @@ pub unsafe fn cstr_to_str(c_buf: *const i8) -> &'static str { // ref. https://doc.rust-lang.org/std/boxed/ #[no_mangle] pub unsafe extern "C" fn NvaClient( + orgid: *const c_char, + api_token: *const c_char, ) -> Box { + let oid_cstr = CStr::from_ptr(orgid); + let oid = oid_cstr.to_str().expect("Bad encoding oid"); + let atk_cstr = CStr::from_ptr(api_token); + let atk = atk_cstr.to_str().expect("Bad encoding atk"); + return Box::new(NavAbilityClient::new( &"https://api.navability.io/graphql".to_owned(), - &"".to_owned(), - &"".to_owned(), + &oid.to_owned(), + &atk.to_owned(), )) } impl Drop for NavAbilityClient { fn drop(&mut self) { - println!("Dropping NavAbilityClient."); + // println!("Dropping NavAbilityClient."); } } +// use libc::{c_char, c_float}; +use ffi_convert::{ + CReprOf, + CDrop, + // CArray, + // AsRust, + // CReprOfError, +}; + +use ::core::{ptr, slice}; + +struct RAgent { + pub id: String, + pub label: String, + pub created_timestamp: String, +} + +#[cfg(feature = "tokio")] +#[repr(C)] +#[derive(CReprOf, CDrop)] +#[target_type(RAgent)] +pub struct CAgent { + pub id: *const libc::c_char, + pub label: *const libc::c_char, + pub created_timestamp: *const libc::c_char +} + +// alt use safer_ffi (decided no) https://users.rust-lang.org/t/pass-a-vec-from-rust-to-c/59184/6 + + +// https://users.rust-lang.org/t/preparing-an-array-of-structs-for-ffi/33411 +#[cfg(feature = "tokio")] +#[repr(C)] +pub struct RVec { + ptr: *mut T, + len: usize, // number of elems +} + +#[cfg(feature = "tokio")] +fn vec_to_ffi ( + v: Vec +) -> RVec { + // Going from Vec<_> to Box<[_]> just drops the (extra) `capacity` + let boxed_slice: Box<[CAgent]> = v.into_boxed_slice(); + let len = boxed_slice.len(); + let fat_ptr: *mut [CAgent] = + Box::into_raw(boxed_slice) + ; + let slim_ptr: *mut CAgent = fat_ptr as _; + RVec:: { ptr: slim_ptr, len } +} + +#[cfg(feature = "tokio")] +unsafe fn free_rvec ( + rvec: RVec +) { + let mut ptr = rvec.ptr; + let len = rvec.len; + + // println!("dropping RVec"); + if ptr.is_null() { + eprintln!("free_boxed_slice() errored: got NULL ptr!"); + ::std::process::abort(); + } + let slice: &mut [T] = + slice::from_raw_parts_mut(ptr, len) + ; + drop(Box::from_raw(slice)); +} + + +#[cfg(feature = "tokio")] +#[no_mangle] +pub unsafe extern "C" fn free_rvec_cagent ( + rvec: RVec +) { + free_rvec::(rvec) +} + +#[cfg(feature = "tokio")] +#[no_mangle] +pub unsafe extern "C" fn list_agents( + _nvacl: *mut NavAbilityClient +) -> RVec { // *mut CAgent { + // if e.is_null() || (*e).is_null() { + // return libc::EINVAL; + // } + // Reconstruct the Error into a box and then drop it so that it's freed. + // to_console_debug(&format!("HERE: {:?}\n",(*_nvacl).apiurl.clone())); + + use std::{os::raw::c_void, ptr}; + let (tx,rx) = std::sync::mpsc::channel::>(); + let _ = fetch_ur_list_tokio(&mut tx.clone(), &(*_nvacl)); + match rx.try_recv() { + Ok(res) => { + // to_console_debug(&format!("got urlist {:?}",res[0])); + let mut ls = Vec::new(); + for r in res { + let ragent = RAgent { + id: r.id.to_string(), + label: r.label.to_string(), + created_timestamp: r.created_timestamp.to_string(), + }; + ls.push(CAgent::c_repr_of(ragent).unwrap()); + } + return vec_to_ffi(ls) + } + Err(e) => {} + } + + return RVec:: { ptr: ptr::null_mut(), len: 0 as usize } + // () +} + + + // Take ownership via passing by value, i.e. runs drop on fn exit. Option for null case. #[no_mangle] pub extern "C" fn free_NvaClient( _: Option> ) {} + #[no_mangle] pub unsafe extern "C" fn get_apiurl(n: &NavAbilityClient) -> *mut c_char { return convert_str(&n.apiurl) } #[no_mangle] -pub unsafe extern "C" fn drop_cstr2(pointer: *mut c_char) -> () { +pub unsafe extern "C" fn drop_cstr(pointer: *mut c_char) -> () { drop(CString::from_raw(pointer)); } diff --git a/test/test.c b/test/test.c index 4b0988d..545587a 100644 --- a/test/test.c +++ b/test/test.c @@ -8,17 +8,26 @@ // Sample library usage. int main(void) { + const char* oid = getenv ("NVA_ORG_ID"); + printf("NVA_ORG_ID: %s\n", (oid != NULL) ? oid : "getenv returned NULL"); + const char* atk = getenv ("NVA_API_TOKEN"); + printf("NVA_API_TOKEN: %s\n", (atk != NULL) ? atk : "getenv returned NULL"); + NavAbilityClient* nvacl = NULL; - nvacl = NvaClient(); + nvacl = NvaClient(oid,atk); char* api = get_apiurl(nvacl); printf("nvacl.apiurl = %s\n", api); + RVec_CAgent agents = list_agents(nvacl); + printf("agent0.label = %s\n", agents.ptr[0].label); + + // drop_cstr2(api); + free_rvec_cagent(agents); free_NvaClient(nvacl); - return 0; } From ce80204efaec1da91c6807dae6b1ee5011e1758f Mon Sep 17 00:00:00 2001 From: dehann Date: Thu, 16 Jan 2025 19:45:58 -0800 Subject: [PATCH 3/6] CNavAbilityClient and impr test --- include/NavAbilitySDK.h | 6 ++++-- src/lib.rs | 9 ++++++--- test/test.c | 6 ++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/NavAbilitySDK.h b/include/NavAbilitySDK.h index 090200c..72b59f9 100644 --- a/include/NavAbilitySDK.h +++ b/include/NavAbilitySDK.h @@ -18,11 +18,13 @@ typedef struct RVec_CAgent { size_t len; } RVec_CAgent; -struct NavAbilityClient *NvaClient(const char *orgid, const char *api_token); +struct NavAbilityClient *CNavAbilityClient(const char *api_url, + const char *orgid, + const char *api_token); void drop_cstr(char *pointer); -void free_NvaClient(struct NavAbilityClient*); +void free_CNavAbilityClient(struct NavAbilityClient*); void free_rvec_cagent(struct RVec_CAgent rvec); diff --git a/src/lib.rs b/src/lib.rs index fb464c7..cc1e75c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1567,17 +1567,20 @@ pub unsafe fn cstr_to_str(c_buf: *const i8) -> &'static str { // ref. https://doc.rust-lang.org/std/boxed/ #[no_mangle] -pub unsafe extern "C" fn NvaClient( +pub unsafe extern "C" fn CNavAbilityClient( + api_url: *const c_char, orgid: *const c_char, api_token: *const c_char, ) -> Box { + let capi_url = CStr::from_ptr(api_url); + let url = capi_url.to_str().expect("Bad encoding url"); let oid_cstr = CStr::from_ptr(orgid); let oid = oid_cstr.to_str().expect("Bad encoding oid"); let atk_cstr = CStr::from_ptr(api_token); let atk = atk_cstr.to_str().expect("Bad encoding atk"); return Box::new(NavAbilityClient::new( - &"https://api.navability.io/graphql".to_owned(), + &url.to_owned(), &oid.to_owned(), &atk.to_owned(), )) @@ -1707,7 +1710,7 @@ pub unsafe extern "C" fn list_agents( // Take ownership via passing by value, i.e. runs drop on fn exit. Option for null case. #[no_mangle] -pub extern "C" fn free_NvaClient( +pub extern "C" fn free_CNavAbilityClient( _: Option> ) {} diff --git a/test/test.c b/test/test.c index 545587a..fdd0679 100644 --- a/test/test.c +++ b/test/test.c @@ -8,13 +8,15 @@ // Sample library usage. int main(void) { + const char* url = getenv ("NVA_API_URL"); + printf("NVA_API_URL: %s\n", (url != NULL) ? url : "getenv returned NULL"); const char* oid = getenv ("NVA_ORG_ID"); printf("NVA_ORG_ID: %s\n", (oid != NULL) ? oid : "getenv returned NULL"); const char* atk = getenv ("NVA_API_TOKEN"); printf("NVA_API_TOKEN: %s\n", (atk != NULL) ? atk : "getenv returned NULL"); NavAbilityClient* nvacl = NULL; - nvacl = NvaClient(oid,atk); + nvacl = CNavAbilityClient(url,oid,atk); char* api = get_apiurl(nvacl); @@ -26,7 +28,7 @@ int main(void) { // drop_cstr2(api); free_rvec_cagent(agents); - free_NvaClient(nvacl); + free_CNavAbilityClient(nvacl); return 0; } From d5dbc020c27b3160639dd2cb98e5dedc7eae03e4 Mon Sep 17 00:00:00 2001 From: dehann Date: Thu, 16 Jan 2025 19:54:07 -0800 Subject: [PATCH 4/6] restore CI --- .github/workflows/ci.yml | 2 +- src/lib.rs | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f3f551..2586291 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,4 +27,4 @@ jobs: make generate-cbindgen-cpp # make test-capi env: - NAVABILITY_USERLABEL: "test@wherewhen.ai" + NVA_API_URL: "https://api.navability.io/graphql" diff --git a/src/lib.rs b/src/lib.rs index cc1e75c..ae66493 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1546,7 +1546,7 @@ fn to_console_error( - +#[cfg(feature = "tokio")] use std::ffi::{ CString, CStr @@ -1554,11 +1554,13 @@ use std::ffi::{ use std::os::raw::c_char; +#[cfg(feature = "tokio")] pub unsafe fn convert_str(input: &str) -> *mut c_char { let c_str = CString::new(input).unwrap().into_raw(); return c_str; } +#[cfg(feature = "tokio")] pub unsafe fn cstr_to_str(c_buf: *const i8) -> &'static str { let cstr = CStr::from_ptr(c_buf); return cstr.to_str().expect("success"); @@ -1566,6 +1568,7 @@ pub unsafe fn cstr_to_str(c_buf: *const i8) -> &'static str { // ref. https://doc.rust-lang.org/std/boxed/ +#[cfg(feature = "tokio")] #[no_mangle] pub unsafe extern "C" fn CNavAbilityClient( api_url: *const c_char, @@ -1586,13 +1589,15 @@ pub unsafe extern "C" fn CNavAbilityClient( )) } -impl Drop for NavAbilityClient { - fn drop(&mut self) { - // println!("Dropping NavAbilityClient."); - } -} +// #[cfg(feature = "tokio")] +// impl Drop for NavAbilityClient { +// fn drop(&mut self) { +// println!("Dropping NavAbilityClient."); +// } +// } // use libc::{c_char, c_float}; +#[cfg(feature = "tokio")] use ffi_convert::{ CReprOf, CDrop, @@ -1601,8 +1606,10 @@ use ffi_convert::{ // CReprOfError, }; +#[cfg(feature = "tokio")] use ::core::{ptr, slice}; +#[cfg(feature = "tokio")] struct RAgent { pub id: String, pub label: String, @@ -1709,17 +1716,19 @@ pub unsafe extern "C" fn list_agents( // Take ownership via passing by value, i.e. runs drop on fn exit. Option for null case. +#[cfg(feature = "tokio")] #[no_mangle] pub extern "C" fn free_CNavAbilityClient( _: Option> ) {} - +#[cfg(feature = "tokio")] #[no_mangle] pub unsafe extern "C" fn get_apiurl(n: &NavAbilityClient) -> *mut c_char { return convert_str(&n.apiurl) } +#[cfg(feature = "tokio")] #[no_mangle] pub unsafe extern "C" fn drop_cstr(pointer: *mut c_char) -> () { drop(CString::from_raw(pointer)); From 231e327b36bed318b34dadd169363f3c1bfea91c Mon Sep 17 00:00:00 2001 From: dehann Date: Thu, 16 Jan 2025 20:00:41 -0800 Subject: [PATCH 5/6] fix make bindgen --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 70a1339..d7b7f49 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ test-tokio: test-capi: build-lib cd test && $(MAKE) -generate-cbinden-cpp: +generate-cbindgen-cpp: cbindgen --config cbindgen.toml --crate navabilitysdk --output include/NavAbilitySDK.hpp -generate-cbinden-c: +generate-cbindgen-c: cbindgen --config cbindgen.toml --lang c --crate navabilitysdk --output include/NavAbilitySDK.h \ No newline at end of file From 627347425ff652a6e89a5fea89f991e557ee0705 Mon Sep 17 00:00:00 2001 From: dehann Date: Thu, 16 Jan 2025 20:04:36 -0800 Subject: [PATCH 6/6] require cbindgen --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2586291..93de311 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,8 @@ jobs: run: make build-wasm - name: Build tokio run: make build-tokio + - name: Install Test Deps + run: cargo install --force cbindgen - name: Run tests run: | make test-tokio