#![allow(private_interfaces)]
use crate::runtime::state::ConnectionState;
use crate::runtime::transport::{TransportConfig, TransportError, WebSocketClient};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr;
enum TransportClient {
WebSocket(WebSocketClient),
#[cfg(feature = "webtransport")]
WebTransport(crate::runtime::webtransport::WebTransportClient),
}
impl TransportClient {
async fn connect(&mut self) -> Result<(), TransportError> {
match self {
Self::WebSocket(c) => c.connect().await,
#[cfg(feature = "webtransport")]
Self::WebTransport(c) => c.connect().await,
}
}
async fn disconnect(&mut self) {
match self {
Self::WebSocket(c) => c.disconnect().await,
#[cfg(feature = "webtransport")]
Self::WebTransport(c) => c.disconnect().await,
}
}
async fn send(&self, data: Vec<u8>) -> Result<(), TransportError> {
match self {
Self::WebSocket(c) => c.send(data).await,
#[cfg(feature = "webtransport")]
Self::WebTransport(c) => c.send(data).await,
}
}
async fn receive(&mut self) -> Result<Vec<u8>, TransportError> {
match self {
Self::WebSocket(c) => c.receive().await,
#[cfg(feature = "webtransport")]
Self::WebTransport(c) => c.receive().await,
}
}
async fn state(&self) -> ConnectionState {
match self {
Self::WebSocket(c) => c.state().await,
#[cfg(feature = "webtransport")]
Self::WebTransport(c) => c.state().await,
}
}
}
struct TransportHandle {
client: TransportClient,
runtime: tokio::runtime::Runtime,
last_error: Option<CString>,
}
pub type MottoTransportHandle = *mut TransportHandle;
#[repr(u8)]
pub enum MottoTransportState {
Disconnected = 0,
Connecting = 1,
Connected = 2,
Reconnecting = 3,
Error = 4,
}
impl From<ConnectionState> for MottoTransportState {
fn from(s: ConnectionState) -> Self {
match s {
ConnectionState::Disconnected => MottoTransportState::Disconnected,
ConnectionState::Connecting => MottoTransportState::Connecting,
ConnectionState::Connected => MottoTransportState::Connected,
ConnectionState::Reconnecting => MottoTransportState::Reconnecting,
ConnectionState::Error => MottoTransportState::Error,
}
}
}
pub type MottoTransportCallback = Option<
unsafe extern "C" fn(data: *const u8, data_len: usize, user_data: *mut std::ffi::c_void),
>;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_new(url: *const c_char) -> MottoTransportHandle {
if url.is_null() {
return ptr::null_mut();
}
let url_str = match unsafe { CStr::from_ptr(url) }.to_str() {
Ok(s) => s.to_owned(),
Err(_) => return ptr::null_mut(),
};
let runtime = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
{
Ok(rt) => rt,
Err(_) => return ptr::null_mut(),
};
let config = TransportConfig {
url: url_str.clone(),
..Default::default()
};
let client = if url_str.starts_with("https://") {
#[cfg(feature = "webtransport")]
{
TransportClient::WebTransport(crate::runtime::webtransport::WebTransportClient::new(
config,
))
}
#[cfg(not(feature = "webtransport"))]
{
return ptr::null_mut(); }
} else {
TransportClient::WebSocket(WebSocketClient::new(config))
};
let handle = Box::new(TransportHandle {
client,
runtime,
last_error: None,
});
Box::into_raw(handle)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_free(handle: MottoTransportHandle) {
if !handle.is_null() {
let mut h = unsafe { Box::from_raw(handle) };
h.runtime.block_on(h.client.disconnect());
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_connect(handle: MottoTransportHandle) -> i32 {
let h = unsafe { &mut *handle };
match h.runtime.block_on(h.client.connect()) {
Ok(()) => {
h.last_error = None;
0
}
Err(e) => {
h.last_error = CString::new(e.to_string()).ok();
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_close(handle: MottoTransportHandle) {
let h = unsafe { &mut *handle };
h.runtime.block_on(h.client.disconnect());
h.last_error = None;
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_send(
handle: MottoTransportHandle,
data: *const u8,
data_len: usize,
) -> i32 {
let h = unsafe { &mut *handle };
if data.is_null() || data_len == 0 {
h.last_error = CString::new("data pointer is null or length is zero").ok();
return -1;
}
let buf = unsafe { std::slice::from_raw_parts(data, data_len) }.to_vec();
match h.runtime.block_on(h.client.send(buf)) {
Ok(()) => {
h.last_error = None;
0
}
Err(e) => {
h.last_error = CString::new(e.to_string()).ok();
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_recv(
handle: MottoTransportHandle,
out_data: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
let h = unsafe { &mut *handle };
match h.runtime.block_on(h.client.receive()) {
Ok(data) => {
unsafe { *out_len = data.len() };
let boxed = data.into_boxed_slice();
unsafe { *out_data = Box::into_raw(boxed) as *mut u8 };
h.last_error = None;
0
}
Err(e) => {
unsafe { *out_data = ptr::null_mut() };
unsafe { *out_len = 0 };
h.last_error = CString::new(e.to_string()).ok();
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_recv_free(data: *mut u8, len: usize) {
if !data.is_null() && len > 0 {
let _ = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(data, len)) };
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_state(
handle: MottoTransportHandle,
) -> MottoTransportState {
let h = unsafe { &mut *handle };
let state = h.runtime.block_on(h.client.state());
state.into()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn motto_transport_last_error(handle: MottoTransportHandle) -> *const c_char {
let h = unsafe { &*handle };
match &h.last_error {
Some(err) => err.as_ptr(),
None => ptr::null(),
}
}