rune/module/
type_mut.rs

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