scheme-rs 0.1.0

Embedded scheme for the Rust ecosystem
Documentation
use std::sync::Arc;

use futures::{
    FutureExt,
    future::{BoxFuture, Shared},
};
use scheme_rs_macros::bridge;

use tokio::{
    net::{TcpListener, TcpStream},
    sync::Mutex,
    time::{Duration, sleep},
};

use crate::{
    exceptions::Exception,
    ports::{BufferMode, Port},
    proc::Procedure,
    records::{Record, RecordTypeDescriptor, SchemeCompatible, rtd},
    strings::WideString,
    value::Value,
};

type Future = Shared<BoxFuture<'static, Result<Vec<Value>, Exception>>>;

impl SchemeCompatible for Future {
    fn rtd() -> Arc<RecordTypeDescriptor> {
        rtd!(
            name: "future",
            opaque: true,
            sealed: true,
        )
    }
}

#[bridge(name = "future", lib = "(async)")]
pub async fn make_future(proc: Procedure) -> Result<Vec<Value>, Exception> {
    let future: Future = async move { proc.call(&[]).await }.boxed().shared();
    let future = Value::from_rust_type(future);
    Ok(vec![future])
}

#[bridge(name = "spawn", lib = "(async)")]
pub async fn spawn(task: &Value) -> Result<Vec<Value>, Exception> {
    let task: Procedure = task.clone().try_into()?;
    let task = tokio::task::spawn(async move { task.call(&[]).await });
    let future: Future = async move { task.await.unwrap() }.boxed().shared();
    let future = Value::from(Record::from_rust_type(future));
    Ok(vec![future])
}

#[bridge(name = "await", lib = "(async)")]
pub async fn await_future(future: &Value) -> Result<Vec<Value>, Exception> {
    future.try_to_rust_type::<Future>()?.as_ref().clone().await
}

impl SchemeCompatible for Arc<TcpListener> {
    fn rtd() -> Arc<RecordTypeDescriptor> {
        rtd!(
            name: "tcp-listener",
            opaque: true,
            sealed: true,
        )
    }
}

#[bridge(name = "bind-tcp", lib = "(async)")]
pub async fn bind_tcp(addr: &Value) -> Result<Vec<Value>, Exception> {
    let addr: WideString = addr.clone().try_into()?;
    let listener = TcpListener::bind(&addr.to_string())
        .await
        .map_err(|e| Exception::error(format!("failed to bind to address: {e:?}")))?;
    let listener = Value::from(Record::from_rust_type(Arc::new(listener)));
    Ok(vec![listener])
}

impl SchemeCompatible for Arc<Mutex<TcpStream>> {
    fn rtd() -> Arc<RecordTypeDescriptor> {
        rtd!(
            name: "socket",
            opaque: true,
            sealed: true,
        )
    }
}

#[bridge(name = "accept", lib = "(async)")]
pub async fn accept(listener: &Value) -> Result<Vec<Value>, Exception> {
    let listener = { listener.try_to_rust_type::<Arc<TcpListener>>()?.clone() };
    let (socket, addr) = listener
        .accept()
        .await
        .map_err(|e| Exception::error(format!("Could not accept client: {e:?}")))?;
    let socket = Value::from(Port::new(addr.to_string(), socket, BufferMode::Block, None));
    let addr = Value::from(addr.to_string());
    Ok(vec![socket, addr])
}

#[bridge(name = "sleep", lib = "(async)")]
pub async fn sleep_ms(ms: u64) -> Result<Vec<Value>, Exception> {
    sleep(Duration::from_millis(ms)).await;
    Ok(Vec::new())
}