1use alloc::vec::Vec;
2use core::any::{TypeId, type_name};
3use core::fmt;
4use core::fmt::{Debug, Formatter};
5
6use crate::internal::environment::ProtoEnvironment;
7use crate::internal::function::ProtoFunction;
8use crate::{Attribute, Environment, Module, Path};
9
10#[derive(Clone)]
11pub struct Function {
12 pub(crate) environment: &'static ProtoEnvironment,
13 pub(crate) proto_function: &'static ProtoFunction,
14}
15
16impl Function {
17 pub const fn name(&self) -> &'static str {
18 self.proto_function.name
19 }
20
21 pub const fn module(&self) -> Option<Module> {
22 if let Some(id) = self.proto_function.module {
23 return Some(self.environment.get_module(id));
24 }
25 None
26 }
27
28 pub const fn path(&self) -> &'static Path {
29 &self.proto_function.path
30 }
31
32 pub fn find_attributes_such_that(
33 &self,
34 f: impl Fn(&Attribute) -> bool,
35 ) -> Vec<&'static Attribute> {
36 self.attributes()
37 .into_iter()
38 .filter(|attribute| f(attribute))
39 .collect()
40 }
41
42 pub fn has_attribute_such_that(&self, f: impl Fn(&Attribute) -> bool) -> bool {
43 self.attributes().into_iter().any(f)
44 }
45
46 pub fn same_as<F: 'static>(&self) -> bool {
47 unsafe { (self.proto_function.function)() }.raw.is::<F>()
48 }
49
50 pub fn cast<F: 'static>(&self) -> Result<&'static F, TypeMismatch> {
51 unsafe { (self.proto_function.function)() }
52 .raw
53 .downcast_ref::<F>()
54 .ok_or_else(|| TypeMismatch {
55 type_name: type_name::<F>(),
56 type_id: TypeId::of::<F>(),
57 expected_type_id: (unsafe { (self.proto_function.function)() }).raw.type_id(),
58 })
59 }
60
61 pub fn try_call<F: 'static, R>(&self, invoke: impl FnOnce(&F) -> R) -> Result<R, TypeMismatch> {
62 self.cast::<F>().map(invoke)
63 }
64
65 pub fn call<F: 'static, R>(&self, invoke: impl FnOnce(&F) -> R) -> R {
66 self.try_call::<F, R>(invoke).unwrap()
67 }
68}
69
70impl Debug for Function {
71 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
72 f.debug_struct(type_name::<Self>())
73 .field("proto_function", &self.proto_function)
74 .field("environment", &type_name::<Environment>())
75 .finish()
76 }
77}
78
79impl PartialEq for Function {
80 fn eq(&self, other: &Self) -> bool {
81 core::ptr::eq(self.environment, other.environment)
82 && core::ptr::eq(self.proto_function, other.proto_function)
83 }
84}
85
86impl Eq for Function {}
87
88#[derive(Debug, Eq, PartialEq, Clone)]
89pub struct TypeMismatch {
90 type_name: &'static str,
91 type_id: TypeId,
92 expected_type_id: TypeId,
93}