avid 0.6.1

A plug-and-play scripting language
Documentation
use std::{
    fmt, any,
};

use crate::{
    func::{AvidFunc, Builtin, Callable},
    parser::Operation, Stack, ErrorKind, Builder,
};

// Used in docs
#[allow(unused_imports)]
use crate::Avid;

/// A compiled version of Avid source code, that can be sent between threads.
///
/// # NOTE
/// This cannot be run on its own but can be converted into an Avid instance at runtime
/// with [Avid::from_ast()].
///
/// # Examples
/// Using an [Ast] to compile source code then send it to another thread:
/// ```
/// use avid::*;
/// use std::thread;
///
/// let src = "\"Hello from the other thread!\" print";
///
/// let ast = Ast::new(src).unwrap();
///
/// thread::spawn( move || {
///     let avid = Avid::from_ast(ast);
///     // prints "Hello from the other thread!" to stdout
///     avid.run(None).unwrap();
/// }).join().unwrap();
/// ```
///
/// Using an [Ast] to compile source code and use it for later.
/// ```
/// # fn register_shutdown_hook(_a: &Ast) {}
/// # fn shutdown() {}
/// use avid::*;
///
/// let src = "\"Shutting down now!\" print";
/// let ast = Ast::new(src).unwrap();
///
/// register_shutdown_hook(&ast);
///
/// // Prints "Shutting down now!"
/// shutdown();
/// ```
#[derive(Debug)]
pub struct Ast<'a> {
    pub(crate) ops: Vec<Operation>,
    pub(crate) provided: Vec<Option<SendCallable<'a>>>,
}

impl<'a> Ast<'a> {
    /// A faster way to create an [Ast] from some source code.
    ///
    /// This is equivalent to the following (but shorter for ease of use):
    /// ```
    /// # use avid::Builder;
    /// # let src = "";
    /// Builder::new_ast(src).build();
    /// ```
    ///
    /// If you want to customise the configuration of the Avid instance-to-be, build the [Ast]
    /// with a [Builder::new_ast()] and use the configuration methods on that.
    pub fn new(src: &'a str) -> crate::Result<Self> {
        Builder::new_ast(src).build()
    }
}

assert_send!(Ast);

#[doc(hidden)]
pub enum SendCallable<'a> {
    Builtin(Builtin),
    Unknown {
        name: Option<String>,
        func: Box<dyn SendFunc + 'a>,
    },
}

impl<'a> From<Builtin> for SendCallable<'a> {
    fn from(b: Builtin) -> Self {
        Self::Builtin(b)
    }
}

impl<'a, T: Send + FnMut(&mut Stack) -> crate::Result<(), ErrorKind> + 'a> From<T> for SendCallable<'a> {
    fn from(f: T) -> Self {
        Self::Unknown {
            name: Some(any::type_name::<T>().to_owned()),
            func: Box::new(f) as Box<dyn SendFunc + 'a>
        }
    }
}

impl AvidFunc for SendCallable<'_> {
    fn call(&mut self, stack: &mut Stack) -> crate::Result<(), ErrorKind> {
        match self {
            SendCallable::Builtin(b) => b.call(stack),
            SendCallable::Unknown { name: _, func } => func.call(stack),
        }
    }

    fn name(&self) -> String {
        match self {
            SendCallable::Builtin(b) => b.name(),
            SendCallable::Unknown { name, func } => name.clone().unwrap_or_else(|| func.name()),
        }
    }
}

impl fmt::Debug for SendCallable<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Builtin(arg0) => f.debug_tuple("Builtin").field(arg0).finish(),
            Self::Unknown { name, func } => f
                .debug_struct("Unknown")
                .field("name", &name.clone().unwrap_or_else(|| func.name()))
                .finish(),
        }
    }
}

// impl<'a, 'b> TryFrom<Callable<'b, 'a>> for SendCallable<'a> {
//     type Error = String;

//     fn try_from(value: Callable<'b, 'a>) -> Result<Self, Self::Error> {
//         match value {
//             Callable::Builtin(b) => Ok(Self::Builtin(b)),
//             Callable::Unknown { name, func } => Err(format!(
//                 "Found non-send function: {}!",
//                 name.unwrap_or_else(|| func.name())
//             )),
//             Callable::UnknownSend { name, func } => Ok(Self::Unknown { name, func }),
//             Callable::Borrowed(_) => Err(String::from("Got borrowed function!")),
//         }
//     }
// }

assert_send!(SendCallable);

#[doc(hidden)]
pub trait SendFunc: Send + AvidFunc {}

impl<T: Send + AvidFunc> SendFunc for T {}

impl<'a> From<SendCallable<'a>> for Callable<'a, 'a> {
    fn from(s: SendCallable<'a>) -> Self {
        match s {
            SendCallable::Builtin(b) => Self::Builtin(b),
            SendCallable::Unknown { name, func } => Self::UnknownSend { name, func },
        }
    }
}