crossmist 1.1.2

Efficient and seamless cross-process communication, both synchronously and asynchronously
Documentation
use crossmist::{lambda, Deserializer, FnOnceObject, Object, Serializer};
use std::fmt::Debug;

fn serde<T: Object>(x: &T) -> T {
    let mut s = Serializer::new();
    s.serialize(x);
    let handles = s.drain_handles();
    let data = s.into_vec();
    let mut d = unsafe {
        Deserializer::new(
            data,
            handles
                .into_iter()
                .map(|fd| {
                    #[cfg(unix)]
                    let borrowed = std::os::fd::BorrowedFd::borrow_raw(fd);
                    #[cfg(windows)]
                    let borrowed = std::os::windows::io::BorrowedHandle::borrow_raw(fd.0 as _);
                    borrowed.try_clone_to_owned().unwrap()
                })
                .collect(),
        )
    };
    unsafe { d.deserialize() }.expect("Deserialization failed")
}

fn test_idempotency<T: Object + PartialEq + Debug>(x: T) {
    assert_eq!(serde(&x), x);
}

#[derive(Debug, PartialEq, Object)]
struct SimplePair {
    x: i32,
    y: i32,
}

#[test]
fn simple() {
    test_idempotency(0x123456789abcdefi64);
}

#[test]
fn string() {
    test_idempotency("hello".to_string());
}

#[test]
fn complex_argument() {
    test_idempotency(SimplePair { x: 5, y: 7 })
}

#[test]
fn hole() {
    test_idempotency((1i32, 2u8, 3i32))
}

#[test]
fn boxed() {
    test_idempotency(Box::new(7))
}

#[test]
fn vec_and_box() {
    test_idempotency((vec![1, 2, 3], Box::new([4, 5, 6])))
}

trait Trait: Object {
    fn say(&self) -> String;
}

#[derive(Object)]
struct ImplA(String);

#[derive(Object)]
struct ImplB(i32);

impl Trait for ImplA {
    fn say(&self) -> String {
        format!("ImplA says: {}", self.0)
    }
}

impl Trait for ImplB {
    fn say(&self) -> String {
        format!("ImplB says: {}", self.0)
    }
}

impl Trait for bool {
    fn say(&self) -> String {
        format!("bool says: {}", self)
    }
}

#[test]
fn box_trait() {
    assert_eq!(
        serde(&(Box::new(ImplA("hello".to_string())) as Box<dyn Trait>)).say(),
        "ImplA says: hello"
    );
    assert_eq!(
        serde(&(Box::new(ImplB(5)) as Box<dyn Trait>)).say(),
        "ImplB says: 5"
    );
    assert_eq!(
        serde(&(Box::new(true) as Box<dyn Trait>)).say(),
        "bool says: true"
    );
}

#[test]
fn function() {
    let func: Box<dyn FnOnceObject<(i32, i32), Output = i32>> =
        lambda! { |a: i32, b: i32| -> i32 { a + b } };
    assert_eq!(serde(&func).call_object_box((5, 7)), 12);
}

#[test]
fn bound_function() {
    let a = 5;
    let func: Box<dyn FnOnceObject<(i32,), Output = i32>> =
        lambda! { move(a: i32) |b: i32| -> i32 { a + b } };
    assert_eq!(serde(&func).call_object_box((7,)), 12);
}

#[test]
fn double_bound_function() {
    let a = 5;
    let b = 7;
    let func: Box<dyn FnOnceObject<(), Output = i32>> =
        lambda! { move(a: i32, b: i32) || -> i32 { a + b } };
    assert_eq!(serde(&func).call_object_box(()), 12);
}

#[test]
#[cfg(not(miri))]
fn test_rx() {
    let (mut tx, rx) = crossmist::channel::<i32>().unwrap();
    let mut rx = serde(&rx);
    tx.send(&5).unwrap();
    tx.send(&7).unwrap();
    assert_eq!(rx.recv().unwrap().unwrap(), 5);
    assert_eq!(rx.recv().unwrap().unwrap(), 7);
}

#[test]
#[cfg(not(miri))]
fn test_tx() {
    let (tx, mut rx) = crossmist::channel::<i32>().unwrap();
    let mut tx = serde(&tx);
    tx.send(&5).unwrap();
    tx.send(&7).unwrap();
    assert_eq!(rx.recv().unwrap().unwrap(), 5);
    assert_eq!(rx.recv().unwrap().unwrap(), 7);
}

#[test]
#[cfg(not(miri))]
fn test_duplex() {
    let (mut local, downstream) = crossmist::duplex::<(i32, i32), i32>().unwrap();
    let mut downstream = serde(&downstream);
    for (x, y) in [(5, 7), (100, -1), (53, 2354)] {
        local.send(&(x, y)).unwrap();
        let (x1, y1) = downstream.recv().unwrap().unwrap();
        downstream.send(&(x1 - y1)).unwrap();
        assert_eq!(local.recv().unwrap().unwrap(), x1 - y1);
    }
    drop(local);
    assert!(downstream.recv().unwrap().is_none());
}