ksl 0.1.30

KSL core library and interpreter
Documentation
//! # ksl::builtin::channel
//!
//! Built-in math functions about `Receiver` and `Sender`.
//!
//! - OpenChannel
//! - ReceiveValue
//! - DropReceiver
//! - SendValue
//! - DropSender

use crate::{Environment, eval::apply::eval_apply, impl_native_object, is_invalid_symbol, value::Value};

impl_native_object! {
    rust_type: std::sync::mpsc::Receiver<Value>,
    type_name_const: MPSC_RECEIVER_TYPE_NAME,
    type_name_str: "MpscReceiver",
    with_fn: {
        name: with_mpsc_receiver,
        error_prefix: "ksl::builtin::channel"
    },
    drop_fn: {
        name: drop_receiver,
        func_name: "DropReceiver",
        error_prefix: "ksl::builtin::channel"
    }
}

impl_native_object! {
    rust_type: std::sync::mpsc::Sender<Value>,
    type_name_const: MPSC_SENDER_TYPE_NAME,
    type_name_str: "MpscSender",
    with_fn: {
        name: with_mpsc_sender,
        error_prefix: "ksl::builtin::channel"
    },
    drop_fn: {
        name: drop_sender,
        func_name: "DropSender",
        error_prefix: "ksl::builtin::channel"
    }
}

pub(crate) fn open_channel(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
    if let [rx_val, tx_val] = args {
        match (rx_val, tx_val) {
            (Value::Symbol(rx_sym), Value::Symbol(tx_sym)) => {
                if is_invalid_symbol(rx_sym) || is_invalid_symbol(tx_sym) {
                    Err(std::sync::Arc::from(format!(
                        concat!(
                            "Error[ksl::builtin::OpenChannel]: ",
                            "Rebinding of symbol `{}` or `{}` is not permitted."
                        ),
                        rx_sym, tx_sym
                    )))
                } else {
                    let (tx, rx) = std::sync::mpsc::channel::<Value>();

                    let rx_obj = Value::NativeObject(
                        MPSC_RECEIVER_TYPE_NAME.clone(),
                        std::sync::Arc::new(parking_lot::Mutex::new(rx)),
                    );
                    let tx_obj = Value::NativeObject(
                        MPSC_SENDER_TYPE_NAME.clone(),
                        std::sync::Arc::new(parking_lot::Mutex::new(tx)),
                    );

                    env.store
                        .write()
                        .insert(rx_sym.clone(), std::sync::Arc::new(rx_obj));
                    env.store
                        .write()
                        .insert(tx_sym.clone(), std::sync::Arc::new(tx_obj));

                    Ok(Value::Unit)
                }
            }
            (Value::Symbol(_), e) => Err(std::sync::Arc::from(format!(
                "Error[ksl::builtin::OpenChannel]: Sender must be a symbol, got `{}`.",
                e
            ))),
            (e, _) => Err(std::sync::Arc::from(format!(
                "Error[ksl::builtin::OpenChannel]: Receiver must be a symbol, got `{}`.",
                e
            ))),
        }
    } else {
        Err(std::sync::Arc::from(format!(
            "Error[ksl::builtin::OpenChannel]: Expected 2 parameters, but {} were passed.",
            args.len()
        )))
    }
}

pub(crate) fn receive_value(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
    if let [rx_obj] = args {
        let rx_val = eval_apply(rx_obj, env.clone())?;

        with_mpsc_receiver(&rx_val, |rx| match rx.recv() {
            Ok(val) => Ok(val),
            Err(e) => Err(std::sync::Arc::from(format!(
                "Error[ksl::builtin::ReceiveValue]: Failed to receive value with `{}`.",
                e
            ))),
        })
    } else {
        Err(std::sync::Arc::from(format!(
            "Error[ksl::builtin::ReceiveValue]: Expected 1 parameter, but {} were passed.",
            args.len()
        )))
    }
}

pub(crate) fn send_value(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
    if let [tx_obj, val_obj] = args {
        let tx_val = eval_apply(tx_obj, env.clone())?;
        let val = eval_apply(val_obj, env)?;

        with_mpsc_sender(&tx_val, |tx| match tx.send(val) {
            Ok(()) => Ok(Value::Unit),
            Err(e) => Err(std::sync::Arc::from(format!(
                "Error[ksl::builtin::SendValue]: Failed to send value `{}` with `{}`.",
                val_obj, e
            ))),
        })
    } else {
        Err(std::sync::Arc::from(format!(
            "Error[ksl::builtin::SendValue]: Expected 1 parameter, but {} were passed.",
            args.len()
        )))
    }
}