rune/module/
type_mut.rs

1use core::fmt;
2use core::marker::PhantomData;
3
4use crate::alloc::prelude::*;
5use crate::compile::{ContextError, Docs};
6use crate::function::{Function, Plain};
7use crate::runtime::{FunctionHandler, TypeOf};
8use crate::Item;
9
10use super::{Enum, EnumMut, Fields, TypeConstructor, TypeSpecification, Variant};
11
12/// Handle to a a type inserted into a module which allows for mutation of its
13/// metadata.
14///
15/// This is returned by the following methods:
16/// * [`Module::ty`] - after a type has been inserted.
17/// * [`Module::type_meta`] - to modify type metadata for an already inserted
18///   type.
19///
20/// [`Module::ty`]: super::Module::ty
21/// [`Module::type_meta`]: super::Module::type_meta
22pub struct TypeMut<'a, T>
23where
24    T: ?Sized + TypeOf,
25{
26    pub(super) docs: &'a mut Docs,
27    #[cfg(feature = "doc")]
28    pub(super) deprecated: &'a mut Option<Box<str>>,
29    pub(super) spec: &'a mut Option<TypeSpecification>,
30    pub(super) constructor: &'a mut Option<TypeConstructor>,
31    pub(super) item: &'a Item,
32    pub(super) _marker: PhantomData<T>,
33}
34
35impl<'a, T> TypeMut<'a, T>
36where
37    T: ?Sized + TypeOf,
38{
39    /// Set documentation for an inserted type.
40    ///
41    /// This completely replaces any existing documentation.
42    pub fn docs(self, docs: impl IntoIterator<Item: AsRef<str>>) -> Result<Self, ContextError> {
43        self.docs.set_docs(docs)?;
44        Ok(self)
45    }
46
47    /// Set static documentation.
48    ///
49    /// This completely replaces any existing documentation.
50    pub fn static_docs(self, docs: &'static [&'static str]) -> Result<Self, ContextError> {
51        self.docs.set_docs(docs)?;
52        Ok(self)
53    }
54
55    /// Mark the given type as deprecated.
56    pub fn deprecated<S>(
57        self,
58        #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S,
59    ) -> Result<Self, ContextError>
60    where
61        S: AsRef<str>,
62    {
63        #[cfg(feature = "doc")]
64        {
65            *self.deprecated = Some(deprecated.as_ref().try_into()?);
66        }
67
68        Ok(self)
69    }
70
71    /// Mark the current type as a struct with named fields.
72    pub fn make_named_struct(self, fields: &'static [&'static str]) -> Result<Self, ContextError> {
73        self.make_struct(Fields::Named(fields))
74    }
75
76    /// Mark the current type as a struct with unnamed fields.
77    pub fn make_unnamed_struct(self, fields: usize) -> Result<Self, ContextError> {
78        self.make_struct(Fields::Unnamed(fields))
79    }
80
81    /// Mark the current type as an empty struct.
82    pub fn make_empty_struct(self) -> Result<Self, ContextError> {
83        self.make_struct(Fields::Empty)
84    }
85
86    /// Mark the current type as an enum.
87    pub fn make_enum(
88        self,
89        variants: &'static [&'static str],
90    ) -> Result<EnumMut<'a, T>, ContextError> {
91        let old = self.spec.replace(TypeSpecification::Enum(Enum {
92            variants: variants.iter().copied().map(Variant::new).try_collect()?,
93        }));
94
95        if old.is_some() {
96            return Err(ContextError::ConflictingTypeMeta {
97                item: self.item.try_to_owned()?,
98                type_info: T::type_info(),
99            });
100        }
101
102        let Some(TypeSpecification::Enum(enum_)) = self.spec.as_mut() else {
103            panic!("Not an enum");
104        };
105
106        Ok(EnumMut {
107            docs: self.docs,
108            enum_,
109            _marker: PhantomData,
110        })
111    }
112
113    /// Register a constructor method for the current type.
114    pub fn constructor<F, A>(self, constructor: F) -> Result<Self, ContextError>
115    where
116        F: Function<A, Plain, Return = T>,
117    {
118        if self.constructor.is_some() {
119            return Err(ContextError::ConstructorConflict {
120                type_info: T::type_info(),
121            });
122        }
123
124        let handler: FunctionHandler = FunctionHandler::new(move |stack, addr, args, output| {
125            constructor.call(stack, addr, args, output)
126        })?;
127
128        *self.constructor = Some(TypeConstructor {
129            handler,
130            args: F::ARGS,
131        });
132
133        Ok(self)
134    }
135
136    fn make_struct(self, fields: Fields) -> Result<Self, ContextError> {
137        let old = self.spec.replace(TypeSpecification::Struct(fields));
138
139        if old.is_some() {
140            return Err(ContextError::ConflictingTypeMeta {
141                item: self.item.try_to_owned()?,
142                type_info: T::type_info(),
143            });
144        }
145
146        Ok(self)
147    }
148}
149
150impl<T> fmt::Debug for TypeMut<'_, T>
151where
152    T: TypeOf,
153{
154    #[inline]
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        f.debug_struct("TypeMut").finish_non_exhaustive()
157    }
158}