use std::ffi::{CStr, CString};
use std::fmt;
use std::os::raw::c_int;
use std::str::FromStr;
use crate::env::with_raw_stdenv;
use crate::{sys, Error, Link};
pub struct LinkServer {
raw_link_server: sys::WSLinkServer,
}
pub struct Incoming<'a> {
server: &'a LinkServer,
}
impl LinkServer {
pub fn bind<A: std::net::ToSocketAddrs>(addrs: A) -> Result<Self, Error> {
let addrs = addrs.to_socket_addrs().map_err(|err| {
Error::custom(format!("error binding LinkServer to address: {}", err))
})?;
crate::for_each_addr(addrs.collect(), |addr| {
let mut err: std::os::raw::c_int = sys::MLEOK;
let iface = CString::new(addr.ip().to_string())
.expect("failed to create CString from LinkServer interface");
let raw_link_server: sys::WSLinkServer =
with_raw_stdenv(|raw_stdenv| unsafe {
sys::WSNewLinkServerWithPortAndInterface(
raw_stdenv,
addr.port(),
iface.as_ptr(),
std::ptr::null_mut(),
&mut err,
)
})?;
if raw_link_server.is_null() || err != sys::MLEOK {
return Err(Error::from_code(err));
}
return Ok(LinkServer { raw_link_server });
})
}
pub fn new(port: u16) -> Result<Self, Error> {
let mut err: std::os::raw::c_int = sys::MLEOK;
let raw_server: sys::WSLinkServer = with_raw_stdenv(|raw_stdenv| unsafe {
sys::WSNewLinkServerWithPort(raw_stdenv, port, std::ptr::null_mut(), &mut err)
})?;
if raw_server.is_null() || err != sys::MLEOK {
return Err(Error::from_code(err));
}
Ok(LinkServer {
raw_link_server: raw_server,
})
}
pub fn new_with_callback<F>(port: u16, callback: F) -> Result<Self, Error>
where
F: FnMut(Link) + Send + Sync + 'static,
{
let mut err: std::os::raw::c_int = sys::MLEOK;
let raw_server: sys::WSLinkServer = with_raw_stdenv(|raw_stdenv| unsafe {
sys::WSNewLinkServerWithPort(
raw_stdenv,
port,
Box::into_raw(Box::new(callback)) as *mut std::ffi::c_void,
&mut err,
)
})?;
if raw_server.is_null() || err != sys::MLEOK {
return Err(Error::from_code(err));
}
unsafe {
sys::WSRegisterCallbackFunctionWithLinkServer(
raw_server,
Some(callback_trampoline::<F>),
)
}
Ok(LinkServer {
raw_link_server: raw_server,
})
}
pub fn port(&self) -> u16 {
self.try_port()
.unwrap_or_else(|err| panic!("WSPortFromLinkServer failed: {}", err))
}
pub fn try_port(&self) -> Result<u16, Error> {
let mut err: std::os::raw::c_int = sys::MLEOK;
let port: u16 =
unsafe { sys::WSPortFromLinkServer(self.raw_link_server, &mut err) };
if err != sys::MLEOK {
return Err(Error::from_code(err));
}
Ok(port)
}
pub fn interface(&self) -> std::net::IpAddr {
self.try_interface()
.unwrap_or_else(|err| panic!("WSInterfaceFromLinkServer failed: {}", err))
}
pub fn try_interface(&self) -> Result<std::net::IpAddr, Error> {
let mut err: c_int = sys::MLEOK;
let iface_cstr =
unsafe { sys::WSInterfaceFromLinkServer(self.raw_link_server, &mut err) };
if iface_cstr.is_null() || err != sys::MLEOK {
return Err(Error::from_code(err));
}
let iface: String = unsafe {
let iface = CStr::from_ptr(iface_cstr);
match iface.to_str() {
Ok(str) => str.to_string(),
Err(utf8_error) => {
sys::WSReleaseInterfaceFromLinkServer(
self.raw_link_server,
iface_cstr,
);
return Err(Error::custom(format!(
"LinkServer interface could not be converted to UTF-8 string (error: {}, lossy: '{}')",
utf8_error,
iface.to_string_lossy()
)));
},
}
};
unsafe {
sys::WSReleaseInterfaceFromLinkServer(self.raw_link_server, iface_cstr);
};
match std::net::IpAddr::from_str(iface.as_str()) {
Ok(ip) => Ok(ip),
Err(err) => Err(Error::custom(format!(
"unable to parse LinkServer interface ({}) as IpAddr: {}",
iface, err
))),
}
}
pub fn close(self) {
}
pub fn accept(&self) -> Result<Link, Error> {
let mut err: c_int = sys::MLEOK;
let raw_link = unsafe {
sys::WSWaitForNewLinkFromLinkServer(self.raw_link_server, &mut err)
};
if raw_link.is_null() || err != sys::MLEOK {
return Err(Error::from_code(err));
}
let link = unsafe { Link::unchecked_new(raw_link) };
Ok(link)
}
pub fn incoming(&self) -> Incoming<'_> {
Incoming { server: self }
}
pub fn raw_link_server(&self) -> sys::WSLinkServer {
self.raw_link_server
}
}
extern "C" fn callback_trampoline<F: FnMut(Link) + Send + Sync + 'static>(
raw_link_server: sys::WSLinkServer,
raw_link: sys::WSLINK,
) {
let mut err: std::os::raw::c_int = sys::MLEOK;
let user_closure: &mut F;
let link: Link;
unsafe {
let raw_user_closure: *mut std::ffi::c_void =
sys::WSContextFromLinkServer(raw_link_server, &mut err);
user_closure = &mut *(raw_user_closure as *mut F);
link = Link::unchecked_new(raw_link);
}
user_closure(link);
}
impl Drop for LinkServer {
fn drop(&mut self) {
let LinkServer { raw_link_server } = *self;
unsafe {
sys::WSShutdownLinkServer(raw_link_server);
}
}
}
impl fmt::Debug for LinkServer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"LinkServer(Port: {}, Interface: {})",
self.port(),
self.interface()
)
}
}
impl<'a> Iterator for Incoming<'a> {
type Item = Result<Link, Error>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.server.accept())
}
}