avid 0.6.1

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

use crate::{func::Callable, Stack, ErrorKind};
#[allow(unused_imports)]
// Used by doc comments
use crate::{Avid, Builder};

/// All of the functions promised by [Builder::promise()], built by [PromiseBuilder::build()].
///
/// See the examples on [Builder::promise()] for more information on why this can be useful.
///
/// This *must* contain all of the functions that were promised when the Avid instance was constructed.
/// If it does not, an error will be thrown.
pub struct Promises<'f> {
    pub(crate) fns: HashMap<String, Callable<'f, 'f>>,
}

impl Promises<'_> {
    /// Gets the number of stored promises
    ///
    /// # Examples
    /// ```
    /// use avid::*;
    ///
    /// let promises = PromiseBuilder::new()
    ///     .add_promise("push-one", |stack| {
    ///         stack.push(Object::Num(1));
    ///         Ok(())
    ///     })
    ///     .add_promise("push-two", |stack| {
    ///         stack.push(Object::Num(2));
    ///         Ok(())
    ///     }).build();
    ///
    /// assert_eq!(promises.count(), 2);
    /// ```
    pub fn count(&self) -> usize {
        self.fns.len()
    }
}

impl fmt::Debug for Promises<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
        let new_fns = self
            .fns
            .keys()
            .map(|x| (x, "<fn>"))
            .collect::<HashMap<_, _>>();
        write!(f, "{new_fns:?}")
    }
}

/// The way to construct [Promises].
///
/// # Examples
/// ```
/// use avid::*;
///
/// let src = "0 while 1 + dup 6 != do get-log-accessed print end";
///
/// let avid = Builder::new(src)
///     .promise("get-log-accessed")
///     .build().unwrap();
///
/// // How many times the log was accessed.
/// let mut log_accessed = 0;
///
/// fn log() {}
///
/// let mut promises = PromiseBuilder::new()
///     .add_promise("get-log-accessed", |stack| {
///         log();
///         log_accessed += 1;
///         let msg = format!("The log has been accessed {log_accessed} times!\n");
///         stack.push(Object::String(msg));
///         Ok(())
///     }).build();
///
/// // Prints:
/// /*
/// The log has been accessed 1 times!;
/// The log has been accessed 2 times!;
/// The log has been accessed 3 times!;
/// The log has been accessed 4 times!;
/// The log has been accessed 5 times!;
/// */
/// avid.run(Some(&mut promises)).unwrap();
/// ```
#[derive(Default)]
pub struct PromiseBuilder<'f> {
    pub(crate) fns: HashMap<String, Callable<'f, 'f>>,
}

impl<'f> PromiseBuilder<'f> {
    /// Consumes the builder and returns the new [Promises] struct.
    ///
    /// # Examples
    /// ```
    /// use avid::*;
    ///
    /// let src = "\"Hello, World!\" eprint";
    /// let avid = Builder::new(src)
    ///     .promise("eprint")
    ///     .build().unwrap();
    ///
    /// let mut promises = PromiseBuilder::new()
    ///     .add_promise("eprint", |stack| {
    ///         let [arg] = stack.pop()?;
    ///         eprintln!("{arg:?}");
    ///         Ok(())
    ///     }).build();
    ///
    /// // Prints "Hello, World!" to stderr.
    /// avid.run(Some(&mut promises)).unwrap();
    /// ```
    pub fn build(self) -> Promises<'f> {
        Promises { fns: self.fns }
    }

    /// Creates a new, empty PromiseBuilder.
    ///
    /// # Examples
    /// ```
    /// use avid::*;
    ///
    /// let promises = PromiseBuilder::new().build();
    ///
    /// assert_eq!(promises.count(), 0);
    /// ```
    pub fn new() -> Self {
        Self {
            fns: HashMap::new(),
        }
    }

    /// Adds a promise and its implementation.
    ///
    /// When [Avid::run()] or [Avid::run_mut()] is called with the [Promises] made by this
    /// PromiseBuilder, this promise and the provided implementation will be available to
    /// use.
    ///
    /// # Examples
    /// ```
    /// use avid::*;
    ///
    /// let src = "\"Hello, World!\" eprint";
    /// let avid = Builder::new(src)
    ///     .promise("eprint")
    ///     .build().unwrap();
    ///
    /// let mut promises = PromiseBuilder::new()
    ///     .add_promise("eprint", |stack| {
    ///         let [arg] = stack.pop()?;
    ///         eprintln!("{arg:?}");
    ///         Ok(())
    ///     }).build();
    ///
    /// // Prints "Hello, World!" to stderr.
    /// avid.run(Some(&mut promises)).unwrap();
    /// ```
    pub fn add_promise<N, F>(mut self, name: N, func: F) -> Self
    where
        N: ToString,
        F: 'f + FnMut(&mut Stack) -> crate::Result<(), ErrorKind>,
    {
        let name = name.to_string();

        let func = Callable::Unknown {
            name: Some(name.clone()),
            func: Box::new(func),
        };

        self.fns.insert(name, func);
        self
    }
}