bevy_mod_scripting_core/bindings/function/
namespace.rs

1//! A module for managing namespaces for functions
2
3use crate::{
4    bindings::function::script_function::{AppScriptFunctionRegistry, ScriptFunction},
5    docgen::info::GetFunctionInfo,
6};
7use bevy::{
8    prelude::{AppTypeRegistry, World},
9    reflect::{GetTypeRegistration, Reflect},
10};
11use std::{any::TypeId, borrow::Cow, marker::PhantomData};
12
13use super::type_dependencies::GetFunctionTypeDependencies;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Reflect)]
16/// A namespace for functions
17pub enum Namespace {
18    /// The function is registered in the global namespace, i.e. with no namespace.
19    /// In practice functions in this namespace should be callable directly by their name, i.e. `my_function()`
20    #[default]
21    Global,
22    /// The function is registered in the namespace corresponding to the given type.
23    /// In practice functions in this namespace should be callable by their qualified name, i.e. `MyType.my_function()`
24    OnType(TypeId),
25}
26
27/// A type which implements [`IntoNamespace`] by always converting to the global namespace
28pub struct GlobalNamespace;
29
30/// A type convertible to a [`Namespace`]
31pub trait IntoNamespace {
32    /// Converts this type into a [`Namespace`]
33    fn into_namespace() -> Namespace;
34}
35
36impl<T: ?Sized + 'static> IntoNamespace for T {
37    fn into_namespace() -> Namespace {
38        if TypeId::of::<T>() == TypeId::of::<GlobalNamespace>() {
39            Namespace::Global
40        } else {
41            Namespace::OnType(TypeId::of::<T>())
42        }
43    }
44}
45
46/// A type which implements [`IntoNamespace`] by always converting to the global namespace
47impl Namespace {
48    /// Returns the prefix for this namespace
49    pub fn prefix(self) -> Cow<'static, str> {
50        match self {
51            Namespace::Global => Cow::Borrowed(""),
52            Namespace::OnType(type_id) => Cow::Owned(format!("{:?}::", type_id)),
53        }
54    }
55
56    /// Returns the fully qualified name of a function in this namespace
57    pub fn function_name<I: Into<Cow<'static, str>>>(self, name: I) -> Cow<'static, str> {
58        match self {
59            Namespace::Global => name.into(),
60            Namespace::OnType(type_id) => Cow::Owned(format!("{:?}::{}", type_id, name.into())),
61        }
62    }
63}
64
65/// A convenience builder for registering multiple functions in a namespace
66pub struct NamespaceBuilder<'a, N> {
67    /// phantom data to reference the namespace type
68    namespace: PhantomData<N>,
69    /// a cached reference to the world
70    pub world: &'a mut World,
71}
72
73impl<'a, S: IntoNamespace> NamespaceBuilder<'a, S> {
74    /// Creates a new `NamespaceBuilder` that will register functions in the namespace corresponding to the given type
75    /// It will also register the type itself in the type registry.
76    pub fn new(world: &'a mut World) -> Self
77    where
78        S: GetTypeRegistration,
79    {
80        {
81            let registry = world.get_resource_or_init::<AppTypeRegistry>();
82            let mut registry = registry.write();
83            registry.register::<S>();
84        }
85        Self {
86            namespace: Default::default(),
87            world,
88        }
89    }
90
91    /// Prefer using the `register` method on the `NamespaceBuilder` instead
92    pub fn new_unregistered(world: &'a mut World) -> Self {
93        Self {
94            namespace: Default::default(),
95            world,
96        }
97    }
98
99    /// Registers a function in the namespace
100    pub fn register<'env, N, F, M>(&mut self, name: N, function: F) -> &mut Self
101    where
102        N: Into<Cow<'static, str>>,
103        F: ScriptFunction<'env, M> + GetFunctionTypeDependencies<M> + GetFunctionInfo<M>,
104    {
105        self.register_inner(name, function, None, None)
106    }
107
108    /// Registers a function in the namespace with a docstring
109    pub fn register_documented<'env, N, F, M>(
110        &mut self,
111        name: N,
112        function: F,
113        docstring: &'static str,
114        arg_names: &'static [&'static str],
115    ) -> &mut Self
116    where
117        N: Into<Cow<'static, str>>,
118        F: ScriptFunction<'env, M> + GetFunctionTypeDependencies<M> + GetFunctionInfo<M>,
119    {
120        self.register_inner(name, function, Some(docstring), Some(arg_names))
121    }
122
123    fn register_inner<'env, N, F, M>(
124        &mut self,
125        name: N,
126        function: F,
127        docstring: Option<&'static str>,
128        arg_names: Option<&'static [&'static str]>,
129    ) -> &mut Self
130    where
131        N: Into<Cow<'static, str>>,
132        F: ScriptFunction<'env, M> + GetFunctionTypeDependencies<M> + GetFunctionInfo<M>,
133    {
134        {
135            {
136                let mut registry = self
137                    .world
138                    .get_resource_or_init::<AppScriptFunctionRegistry>();
139                let mut registry = registry.write();
140                registry.register_with_arg_names(
141                    S::into_namespace(),
142                    name,
143                    function,
144                    docstring.unwrap_or_default(),
145                    arg_names.unwrap_or(&[]),
146                );
147            }
148            {
149                let type_registry = self.world.get_resource_or_init::<AppTypeRegistry>();
150                let mut type_registry = type_registry.write();
151                F::register_type_dependencies(&mut type_registry);
152            }
153        }
154        self
155    }
156}