stainless_script 0.1.2

Visual node-based programming language
Documentation
use super::any_class;
use crate::{
    class::Class,
    node::Node,
    socket::{InputSocket, OutputSocket},
    ExecutionContext,
};
use std::{borrow::Cow, fmt::Display, num::ParseIntError, rc::Rc, str::FromStr};
use thiserror::Error;

pub fn print_class() -> Class {
    Class {
        name: "print".into(),
        nodes: vec![Rc::new(Print(PrintVariant {
            ln: true,
            amount: 1,
        })) as Rc<dyn Node>],
        obj_from_str: None,
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct PrintVariant {
    ln: bool,
    amount: u32,
}

impl Display for PrintVariant {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}:{}",
            match self.ln {
                true => "println",
                false => "print",
            },
            self.amount
        )
    }
}

impl FromStr for PrintVariant {
    type Err = PrintVariantParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "print" {
            Ok(Self {
                ln: false,
                amount: 1,
            })
        } else if s == "println" {
            Ok(Self {
                ln: true,
                amount: 1,
            })
        } else if let [print_kind, print_amount] = s.split(':').collect::<Vec<&str>>()[..] {
            let ln = match print_kind {
                "print" => false,
                "println" => true,
                s => return Err(PrintVariantParseError::InvalidPrintKind(s.into())),
            };
            let amount = print_amount.parse()?;
            Ok(Self { ln, amount })
        } else {
            Err(PrintVariantParseError::InvalidVariant(s.into()))
        }
    }
}

#[derive(Debug, Clone, Error)]
enum PrintVariantParseError {
    #[error("Invalid variant: {0}")]
    InvalidVariant(String),
    #[error("Invalid print kind: {0}")]
    InvalidPrintKind(String),
    #[error("Failed to parse amount: {0}")]
    AmountParseError(ParseIntError),
}

impl From<ParseIntError> for PrintVariantParseError {
    fn from(e: ParseIntError) -> Self {
        Self::AmountParseError(e)
    }
}

#[derive(Debug, Clone)]
pub struct Print(PrintVariant);

impl Node for Print {
    fn execute(&self, context: &mut ExecutionContext) -> usize {
        let to_print: String = context
            .get_inputs()
            .iter()
            .map(ToString::to_string)
            .collect::<Vec<String>>()
            .join(" ");
        if self.0.ln {
            println!("{to_print}");
        } else {
            print!("{to_print}");
        };
        0
    }

    fn class(&self) -> Class {
        print_class()
    }

    fn variants(&self) -> Vec<Cow<'_, str>> {
        vec![
            "print".into(),
            "println".into(),
            Cow::Owned(self.0.to_string()),
        ]
    }

    fn current_variant(&self) -> Cow<'_, str> {
        self.0.to_string().into()
    }

    fn set_variant(&mut self, variant: &str) {
        self.0 = variant.parse().unwrap()
    }

    fn accepts_arbitrary_variants(&self) -> bool {
        true
    }

    fn inputs(&self) -> Vec<InputSocket> {
        vec![InputSocket { class: any_class() }; self.0.amount as usize]
    }

    fn outputs(&self) -> Vec<OutputSocket> {
        vec![]
    }

    fn clone_node(&self) -> Rc<dyn Node> {
        Rc::new(self.clone()) as Rc<dyn Node>
    }
}