Skip to main content

fre_rs/
function.rs

1use super::*;
2
3
4/// The function is used to initialize the extension.
5///
6/// Returns [`None`] if no Extension Data is set.
7/// 
8pub type Initializer = fn () -> Option<Box<dyn Any>>;
9
10/// The function receives ownership of the Extension Data,
11/// allowing it to be saved or disposed.
12/// 
13/// The runtime does **NOT** guarantee that this function will be called before process termination.
14/// 
15pub type Finalizer = fn (ext_data: Option<Box<dyn Any>>);
16
17/// The function is used to initialize a [`Context`].
18///
19/// The first return value sets the Context Data.
20/// The second return value sets the methods associated with the [`Context`].
21/// 
22pub type ContextInitializer = fn (ctx: &CurrentContext) -> (Option<Box<dyn Any>>, FunctionSet);
23
24/// The function is called before the Context Data is disposed,
25/// allowing final access and saving data to the Extension Data.
26///
27pub type ContextFinalizer = fn (ctx: &CurrentContext);
28
29/// The function can be associated with a [`Context`] and treated as its method.
30///
31pub type Function <'a> = fn (ctx: &CurrentContext<'a>, func_data: Option<&mut dyn Any>, args: &[as3::Object<'a>]) -> as3::Object<'a>;
32
33
34/// **In typical usage of this crate, instances of this type should not be constructed directly.**
35/// 
36/// The [`crate::function!`] macro should be used to construct this type, as it provides a safer abstraction.
37/// 
38#[derive(Debug)]
39pub struct FunctionImplementation {
40    raw_name: UCStr,
41    raw_func: FREFunction,
42}
43impl FunctionImplementation {
44    pub fn raw_name(&self) -> &UCStr {&self.raw_name}
45    pub fn raw_func(&self) -> FREFunction {self.raw_func}
46    pub const fn new (raw_name: UCStr, raw_func: FREFunction) -> Self {
47        Self { raw_name, raw_func }
48    }
49}
50
51
52/// A collection of functions associated with a specific context type.
53///
54/// This type is used to construct a set of functions for a [`Context`].
55/// Once registered, these functions are referred to as *methods* within this crate,
56/// and can be invoked from ActionScript via `ExtensionContext.call`.
57/// 
58#[derive(Debug)]
59pub struct FunctionSet {
60    list: Vec<FRENamedFunction>,
61    map: HashMap<UCStr, usize>,
62}
63impl FunctionSet {
64    pub fn new () -> Self {
65        Self {
66            list: Vec::new(),
67            map: HashMap::new(),
68        }
69    }
70    pub fn with_capacity (capacity: usize) -> Self{
71        Self {
72            list: Vec::with_capacity(capacity),
73            map: HashMap::with_capacity(capacity),
74        }
75    }
76
77    /// Adds a function that can be registered as a method.
78    ///
79    /// - `name`: The method name. If [`None`], the raw name of `func_impl` is used.
80    /// - `func_data`: Data associated with this method. It will be dropped after [`ContextFinalizer`] returns.
81    /// - `func_impl`: The method implementation created by the [`crate::function!`] macro.
82    /// 
83    /// # Panics
84    ///
85    /// Panics if a function with the same `name` has already been added.
86    ///
87    /// Callers must ensure that each `name` is unique within this set. 
88    /// 
89    pub fn add (
90        &mut self,
91        name: Option<UCStr>,
92        func_data: Option<Box<dyn Any>>,
93        func_impl: &FunctionImplementation,
94    ) {
95        let name = name.unwrap_or(func_impl.raw_name.clone());
96        let index = self.list.len();
97        self.list.push(FRENamedFunction {
98            name: name.as_ptr(),
99            functionData: if let Some(func_data) = func_data {crate::data::into_raw(func_data).as_ptr()} else {FREData::default()},
100            function: func_impl.raw_func,
101        });
102        let r = self.map.insert(name, index);
103        assert!(r.is_none(), "Method name conflict.");
104    }
105}
106impl Drop for FunctionSet {
107    fn drop(&mut self) {
108        self.list.iter()
109            .map(|i| i.functionData)
110            .for_each(|d| {
111                if let Some(d) = NonNullFREData::new(d) {
112                    unsafe {crate::data::drop_from(d)};
113                }
114            });
115    }
116}
117
118
119#[derive(Debug)]
120pub(super) struct MethodSet {
121    registry: Box<[FRENamedFunction]>,
122    dictionary: HashMap<UCStr, usize>,
123}
124impl MethodSet {
125    /// ## Borrow
126    pub(super) fn get(&self, name: &str) -> Option<(FREFunction, FREData)> {
127        self.dictionary.get(name)
128            .map(|index| {
129                let i = &(self.registry[*index]);
130                (i.function, i.functionData)
131            })
132    }
133}
134impl Drop for MethodSet {
135    fn drop(&mut self) {
136        self.registry.iter()
137            .map(|i| i.functionData)
138            .for_each(|d| {
139                if let Some(d) = NonNullFREData::new(d) {
140                    unsafe {crate::data::drop_from(d)};
141                }
142            });
143    }
144}
145impl From<FunctionSet> for MethodSet {
146    fn from(mut value: FunctionSet) -> Self {
147        let registry = std::mem::take(&mut value.list).into_boxed_slice();
148        let dictionary = std::mem::take(&mut value.map);
149        Self { registry, dictionary }
150    }
151}
152impl AsRef<[FRENamedFunction]> for MethodSet {
153    fn as_ref(&self) -> &[FRENamedFunction] {self.registry.as_ref()}
154}