use alloc::vec::Vec;
use core::any::{TypeId, type_name};
use core::fmt;
use core::fmt::{Debug, Formatter};
use crate::internal::environment::ProtoEnvironment;
use crate::internal::function::ProtoFunction;
use crate::{Attribute, Environment, Module, Path};
#[derive(Clone)]
pub struct Function {
pub(crate) environment: &'static ProtoEnvironment,
pub(crate) proto_function: &'static ProtoFunction,
}
impl Function {
pub const fn name(&self) -> &'static str {
self.proto_function.name
}
pub const fn module(&self) -> Option<Module> {
if let Some(id) = self.proto_function.module {
return Some(self.environment.get_module(id));
}
None
}
pub const fn path(&self) -> &'static Path {
&self.proto_function.path
}
pub fn find_attributes_such_that(
&self,
f: impl Fn(&Attribute) -> bool,
) -> Vec<&'static Attribute> {
self.attributes()
.into_iter()
.filter(|attribute| f(attribute))
.collect()
}
pub fn has_attribute_such_that(&self, f: impl Fn(&Attribute) -> bool) -> bool {
self.attributes().into_iter().any(f)
}
pub fn same_as<F: 'static>(&self) -> bool {
unsafe { (self.proto_function.function)() }.raw.is::<F>()
}
pub fn cast<F: 'static>(&self) -> Result<&'static F, TypeMismatch> {
unsafe { (self.proto_function.function)() }
.raw
.downcast_ref::<F>()
.ok_or_else(|| TypeMismatch {
type_name: type_name::<F>(),
type_id: TypeId::of::<F>(),
expected_type_id: (unsafe { (self.proto_function.function)() }).raw.type_id(),
})
}
pub fn try_call<F: 'static, R>(&self, invoke: impl FnOnce(&F) -> R) -> Result<R, TypeMismatch> {
self.cast::<F>().map(invoke)
}
pub fn call<F: 'static, R>(&self, invoke: impl FnOnce(&F) -> R) -> R {
self.try_call::<F, R>(invoke).unwrap()
}
}
impl Debug for Function {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct(type_name::<Self>())
.field("proto_function", &self.proto_function)
.field("environment", &type_name::<Environment>())
.finish()
}
}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.environment, other.environment)
&& core::ptr::eq(self.proto_function, other.proto_function)
}
}
impl Eq for Function {}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct TypeMismatch {
type_name: &'static str,
type_id: TypeId,
expected_type_id: TypeId,
}