bevy_mod_scripting_bindings/function/
namespace.rs

1//! A module for managing namespaces for functions
2
3use crate::{
4    DummyScriptFunctionRegistry, ScriptFunctionRegistryArc,
5    docgen::info::GetFunctionInfo,
6    function::script_function::{AppScriptFunctionRegistry, ScriptFunction},
7};
8use ::bevy_reflect::{GetTypeRegistration, Reflect};
9use bevy_ecs::{reflect::AppTypeRegistry, world::World};
10use bevy_mod_scripting_derive::DebugWithTypeInfo;
11use bevy_mod_scripting_display::{DisplayWithTypeInfo, GetTypeInfo, WithTypeInfo};
12use std::{any::TypeId, borrow::Cow, marker::PhantomData};
13
14use super::type_dependencies::GetFunctionTypeDependencies;
15
16/// A namespace for functions
17#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Reflect, DebugWithTypeInfo)]
18#[debug_with_type_info(bms_display_path = "bevy_mod_scripting_display")]
19pub enum Namespace {
20    /// The function is registered in the global namespace, i.e. with no namespace.
21    /// In practice functions in this namespace should be callable directly by their name, i.e. `my_function()`
22    #[default]
23    Global,
24    /// The function is registered in the namespace corresponding to the given type.
25    /// In practice functions in this namespace should be callable by their qualified name, i.e. `MyType.my_function()`
26    OnType(TypeId),
27}
28
29/// A type which implements [`IntoNamespace`] by always converting to the global namespace
30pub struct GlobalNamespace;
31
32/// A type convertible to a [`Namespace`]
33pub trait IntoNamespace {
34    /// Converts this type into a [`Namespace`]
35    fn into_namespace() -> Namespace;
36}
37
38impl<T: ?Sized + 'static> IntoNamespace for T {
39    fn into_namespace() -> Namespace {
40        if TypeId::of::<T>() == TypeId::of::<GlobalNamespace>() {
41            Namespace::Global
42        } else {
43            Namespace::OnType(TypeId::of::<T>())
44        }
45    }
46}
47
48/// A type which implements [`IntoNamespace`] by always converting to the global namespace
49#[profiling::all_functions]
50impl Namespace {
51    /// Returns the prefix for this namespace
52    pub fn prefix(self) -> Cow<'static, str> {
53        match self {
54            Namespace::Global => Cow::Borrowed(""),
55            Namespace::OnType(type_id) => Cow::Owned(format!("{type_id:?}::")),
56        }
57    }
58
59    /// Returns the fully qualified name of a function in this namespace
60    pub fn function_name<I: Into<Cow<'static, str>>>(self, name: I) -> Cow<'static, str> {
61        match self {
62            Namespace::Global => name.into(),
63            Namespace::OnType(type_id) => Cow::Owned(format!("{:?}::{}", type_id, name.into())),
64        }
65    }
66}
67
68/// A convenience builder for registering multiple functions in a namespace
69pub struct NamespaceBuilder<'a, N> {
70    /// If true will use the dummy function registry instead
71    registry: ScriptFunctionRegistryArc,
72    /// phantom data to reference the namespace type
73    namespace: PhantomData<N>,
74    /// a cached reference to the world
75    pub world: &'a mut World,
76}
77
78#[profiling::all_functions]
79impl<'a, S: IntoNamespace> NamespaceBuilder<'a, S> {
80    /// Creates a new `NamespaceBuilder` that will register functions in the namespace corresponding to the given type
81    /// It will also register the type itself in the type registry.
82    pub fn new(world: &'a mut World) -> Self
83    where
84        S: GetTypeRegistration,
85    {
86        {
87            let registry = world.get_resource_or_init::<AppTypeRegistry>();
88            let mut registry = registry.write();
89            registry.register::<S>();
90        }
91        Self {
92            registry: world
93                .get_resource_or_init::<AppScriptFunctionRegistry>()
94                .0
95                .clone(),
96            namespace: Default::default(),
97            world,
98        }
99    }
100
101    /// Prefer using the `register` method on the `NamespaceBuilder` instead
102    pub fn new_unregistered(world: &'a mut World) -> Self {
103        Self {
104            registry: world
105                .get_resource_or_init::<AppScriptFunctionRegistry>()
106                .0
107                .clone(),
108            namespace: Default::default(),
109            world,
110        }
111    }
112
113    /// Register functions for this namespace on the dummy function registry instead.
114    ///
115    /// This will appear in documentation but not become callable.
116    pub fn with_dummy_registry(mut self) -> Self {
117        self.registry = self
118            .world
119            .get_resource_or_init::<DummyScriptFunctionRegistry>()
120            .0
121            .clone();
122        self
123    }
124
125    /// Registers a function in the namespace
126    pub fn register<'env, N, F, M>(&mut self, name: N, function: F) -> &mut Self
127    where
128        N: Into<Cow<'static, str>>,
129        F: ScriptFunction<'env, M> + GetFunctionTypeDependencies<M> + GetFunctionInfo<M>,
130    {
131        self.register_inner(name, function, None, None)
132    }
133
134    /// Registers a function in the namespace with a docstring
135    pub fn register_documented<'env, N, F, M>(
136        &mut self,
137        name: N,
138        function: F,
139        docstring: &'static str,
140        arg_names: &'static [&'static str],
141    ) -> &mut Self
142    where
143        N: Into<Cow<'static, str>>,
144        F: ScriptFunction<'env, M> + GetFunctionTypeDependencies<M> + GetFunctionInfo<M>,
145    {
146        self.register_inner(name, function, Some(docstring), Some(arg_names))
147    }
148
149    fn register_inner<'env, N, F, M>(
150        &mut self,
151        name: N,
152        function: F,
153        docstring: Option<&'static str>,
154        arg_names: Option<&'static [&'static str]>,
155    ) -> &mut Self
156    where
157        N: Into<Cow<'static, str>>,
158        F: ScriptFunction<'env, M> + GetFunctionTypeDependencies<M> + GetFunctionInfo<M>,
159    {
160        {
161            {
162                let mut registry = self.registry.write();
163                registry.register_with_arg_names(
164                    S::into_namespace(),
165                    name,
166                    function,
167                    docstring.unwrap_or_default(),
168                    arg_names.unwrap_or(&[]),
169                );
170            }
171            {
172                let type_registry = self.world.get_resource_or_init::<AppTypeRegistry>();
173                let mut type_registry = type_registry.write();
174                F::register_type_dependencies(&mut type_registry);
175            }
176        }
177        self
178    }
179}
180
181impl From<TypeId> for Namespace {
182    fn from(value: TypeId) -> Self {
183        Namespace::OnType(value)
184    }
185}
186
187impl DisplayWithTypeInfo for Namespace {
188    fn display_with_type_info(
189        &self,
190        f: &mut std::fmt::Formatter<'_>,
191        type_info_provider: Option<&dyn GetTypeInfo>,
192    ) -> std::fmt::Result {
193        match self {
194            Namespace::Global => f.write_str("Global Namespace"),
195            Namespace::OnType(type_id) => {
196                write!(
197                    f,
198                    "Namespace for type {}",
199                    WithTypeInfo::new_with_opt_info(type_id, type_info_provider)
200                )
201            }
202        }
203    }
204}