use crate::prelude::*;
use alloc::borrow::Cow;
use core::fmt::Display;
use hashbrown::hash_map;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Library(YarnFnRegistry);
impl Extend<<YarnFnRegistry as IntoIterator>::Item> for Library {
fn extend<T: IntoIterator<Item = (Cow<'static, str>, Box<dyn UntypedYarnFn>)>>(
&mut self,
iter: T,
) {
self.0.extend(iter);
}
}
impl IntoIterator for Library {
type Item = (Cow<'static, str>, Box<dyn UntypedYarnFn>);
type IntoIter = hash_map::IntoIter<Cow<'static, str>, Box<dyn UntypedYarnFn>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Library {
pub fn new() -> Self {
Self::default()
}
pub fn import(&mut self, other: Self) {
self.0.extend(other.0 .0);
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &(dyn UntypedYarnFn))> {
self.0.iter()
}
pub fn get(&self, name: &str) -> Option<&(dyn UntypedYarnFn)> {
self.0.get(name)
}
pub fn generate_unique_visited_variable_for_node(node_name: &str) -> String {
format!("$Yarn.Internal.Visiting.{node_name}")
}
pub fn standard_library() -> Self {
let mut library = yarn_library!(
"string" => <String as From<YarnValue >>::from,
"number" => |value: YarnValue| f32::try_from(value).expect("Failed to convert a Yarn value to a number"),
"bool" => |value: YarnValue| bool::try_from(value).expect("Failed to convert a Yarn value to a bool"),
);
for r#type in [Type::Number, Type::String, Type::Boolean] {
library.add_methods(r#type);
}
library
}
pub fn add_function<Marker, F>(
&mut self,
name: impl Into<Cow<'static, str>>,
function: F,
) -> &mut Self
where
Marker: 'static,
F: YarnFn<Marker> + 'static + Clone,
F::Out: IntoYarnValueFromNonYarnValue + 'static + Clone,
{
self.0.register_function(name, function);
self
}
pub fn contains_function(&self, name: &str) -> bool {
self.0.contains_function(name)
}
pub fn names(&self) -> impl Iterator<Item = &str> {
self.0.names()
}
pub fn functions(&self) -> impl Iterator<Item = &(dyn UntypedYarnFn)> {
self.0.functions()
}
fn add_methods(&mut self, r#type: Type) {
for (name, function) in r#type.methods().into_iter() {
let canonical_name = r#type.get_canonical_name_for_method(name.as_ref());
self.0.add_boxed(canonical_name, function.clone());
}
}
}
impl Display for Library {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut functions: Vec<_> = self.0.iter().collect();
functions.sort_by_key(|(name, _)| name.to_string());
writeln!(f, "{{")?;
for (name, function) in functions {
writeln!(f, " {}: {}", name, function)?;
}
writeln!(f, "}}")?;
Ok(())
}
}
#[macro_export]
macro_rules! yarn_library {
($($name:expr => $function:expr,)*) => {
{
let mut map = Library::default();
$(
map.add_function($name, $function);
)*
map
}
};
}
pub use yarn_library;