diff --git a/Cargo.lock b/Cargo.lock index 8d50945..f1fa8b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -160,6 +171,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -194,6 +211,25 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +[[package]] +name = "cbindgen" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" +dependencies = [ + "clap", + "heck 0.4.1", + "indexmap 1.9.3", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml", +] + [[package]] name = "cc" version = "1.1.20" @@ -229,11 +265,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "codemp" version = "0.7.2" dependencies = [ "async-trait", + "cbindgen", "codemp-proto", "dashmap", "diamond-types", @@ -531,12 +592,27 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.9" @@ -893,7 +969,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -950,7 +1026,7 @@ version = "2.16.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04409e8c2d61995696e44d2181b79b68c1dd41f7e24a17cde60bbd9f54ddddef" dependencies = [ - "bitflags", + "bitflags 2.6.0", "chrono", "ctor", "encoding_rs", @@ -1076,6 +1152,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "overload" version = "0.1.1" @@ -1220,7 +1302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck", + "heck 0.5.0", "itertools", "log", "multimap", @@ -1312,7 +1394,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1be962f0e06da8f8465729ea2cb71a416d2257dff56cbe40a70d3e62a93ae5d1" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "pyo3-build-config", "quote", @@ -1364,7 +1446,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -1438,7 +1520,7 @@ version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1542,7 +1624,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -1682,6 +1764,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.6.1" @@ -1741,6 +1829,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + [[package]] name = "thiserror" version = "1.0.63" @@ -1833,6 +1936,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.6.8" diff --git a/Cargo.toml b/Cargo.toml index 529aec6..b66e8f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,8 @@ serde = { version = "1.0", features = ["derive"], optional = true } napi-build = { version = "2.1", optional = true } # glue (python) pyo3-build-config = { version = "0.22", optional = true } +# glue (C) +cbindgen = { version = "0.24", optional = true } [features] default = [] @@ -73,6 +75,7 @@ java = ["lazy_static", "jni", "tracing-subscriber", "jni-toolbox"] js = ["napi-build", "tracing-subscriber", "napi", "napi-derive"] py-noabi = ["pyo3", "tracing-subscriber", "pyo3-build-config"] py = ["py-noabi", "pyo3/abi3-py38"] +c = ["cbindgen"] lua = ["mlua-codemp-patch", "tracing-subscriber", "lazy_static", "serialize"] lua54 =["lua", "mlua-codemp-patch/lua54"] luajit = ["lua", "mlua-codemp-patch/luajit"] diff --git a/build.rs b/build.rs index 2710634..a764a9a 100644 --- a/build.rs +++ b/build.rs @@ -4,6 +4,9 @@ extern crate napi_build; #[cfg(any(feature = "py", feature = "py-noabi"))] extern crate pyo3_build_config; +#[cfg(feature = "c")] +extern crate cbindgen; + /// The main method of the buildscript, required by some glue modules. fn main() { #[cfg(feature = "js")] @@ -20,7 +23,16 @@ fn main() { { if let Ok("macos") = std::env::var("CARGO_CFG_TARGET_OS").as_deref() { println!("cargo:rustc-cdylib-link-arg=-undefined"); - println!("cargo:rustc-cdylib-link-arg=dynamic_lookup"); - } + println!("cargo:rustc-cdylib-link-arg=dynamic_lookup"); + } + } + + #[cfg(feature = "c")] + { + cbindgen::Builder::new() + .with_crate(std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .generate() + .expect("Unable to generate bindings") + .write_to_file("dist/c/codemp.h"); } } diff --git a/dist/c/codemp.h b/dist/c/codemp.h new file mode 100644 index 0000000..b99c616 --- /dev/null +++ b/dist/c/codemp.h @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +/// A `codemp` client handle. +/// +/// It generates a new UUID and stores user credentials upon connecting. +/// +/// A new [`Client`] can be obtained with [`Client::connect`]. +struct Client; + +/// A currently active shared development environment +/// +/// Workspaces encapsulate a working environment: cursor positions, filetree, user list +/// and more. Each holds a [cursor::Controller] and a map of [buffer::Controller]s. +/// Using a workspace handle, it's possible to receive events (user join/leave, filetree updates) +/// and create/delete/attach to new buffers. +struct Workspace; + +extern "C" { + +Client *Codemp_Client_connect(); + +Workspace *Codemp_Client_join_workspace(Client *client, char *workspace); + +} // extern "C" diff --git a/src/ffi/c/mod.rs b/src/ffi/c/mod.rs new file mode 100644 index 0000000..767bd8b --- /dev/null +++ b/src/ffi/c/mod.rs @@ -0,0 +1,45 @@ +use std::ffi::{c_char, CString}; + +use crate::{api::Config, Client, Workspace}; + +pub(crate) fn tokio() -> &'static tokio::runtime::Runtime { + use std::sync::OnceLock; + static RT: OnceLock = OnceLock::new(); + RT.get_or_init(|| + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("could not create tokio runtime") + ) +} + + +#[no_mangle] // TODO config +pub extern "C" fn Codemp_Client_connect() -> *mut Client { + match tokio() + .block_on(Client::connect(Config::new("", ""))) + { + Ok(c) => Box::into_raw(Box::new(c)), + Err(e) => { + tracing::error!("failed connecting to remote: {e}"); + std::ptr::null_mut() + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn Codemp_Client_join_workspace(client: *mut Client, workspace: *mut c_char) -> *mut Workspace { + let client = unsafe { Box::leak(Box::from_raw(client)) }; + let workspace = unsafe { CString::from_raw(workspace) }.to_string_lossy().to_string(); + + match tokio() + .block_on(client.join_workspace(workspace)) + { + Ok(ws) => Box::into_raw(Box::new(ws)), + Err(e) => { + tracing::error!("failed joining workspace: {e}"); + std::ptr::null_mut() + }, + } + +} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index fa03449..c3e8668 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -58,3 +58,7 @@ pub mod js; /// python bindings, built with [pyo3] #[cfg(any(feature = "py", feature = "py-noabi"))] pub mod python; + +/// c bindings, generated with [cbindgen] +#[cfg(feature = "c")] +pub mod c;