1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use crate::core_types::{GodotString, Variant, VariantType};
use crate::export::{ClassBuilder, ExportInfo, NativeClass, PropertyUsage};

/// Class to construct a signal. Make sure to call [`Self::done()`] in the end.
///
/// Signal parameters can be added with the various `param*()` methods.
/// Keep in mind that unlike function parameters, signal parameters (both their lengths and types)
/// are not statically checked in Godot. The parameter signature you specify is simply to assist you
/// in the editor UI and with auto-generation of GDScript signal handlers.
#[must_use = "SignalBuilder left unbuilt -- did you forget to call done()?"]
pub struct SignalBuilder<'a, C> {
    class_builder: &'a ClassBuilder<C>,
    name: GodotString,
    args: Vec<SignalParam>,
}

impl<'a, C: NativeClass> SignalBuilder<'a, C> {
    pub(super) fn new(class_builder: &'a ClassBuilder<C>, signal_name: GodotString) -> Self {
        Self {
            class_builder,
            name: signal_name,
            args: vec![],
        }
    }

    /// Add a parameter for the signal with a name and type.
    ///
    /// Note that GDScript signal parameters are generally untyped and not checked at runtime.
    /// The type is solely used for UI purposes.
    #[inline]
    pub fn with_param(self, parameter_name: &str, parameter_type: VariantType) -> Self {
        self.with_param_custom(SignalParam {
            name: parameter_name.into(),
            default: Variant::nil(),
            export_info: ExportInfo::new(parameter_type),
            usage: PropertyUsage::DEFAULT,
        })
    }

    /// Add a parameter for the signal with a name and default value.
    ///
    /// The type is inferred from the default value.
    /// Note that GDScript signal parameters are generally untyped and not checked at runtime.
    /// The type is solely used for UI purposes.
    #[inline]
    pub fn with_param_default(self, parameter_name: &str, default_value: Variant) -> Self {
        let variant_type = default_value.get_type();

        self.with_param_custom(SignalParam {
            name: parameter_name.into(),
            default: default_value,
            export_info: ExportInfo::new(variant_type),
            usage: PropertyUsage::DEFAULT,
        })
    }

    /// Add a (untyped) parameter for the signal with a name.
    ///
    /// Types are not required or checked at runtime, but they help for editor UI and auto-generation of signal listeners.
    #[inline]
    pub fn with_param_untyped(self, parameter_name: &str) -> Self {
        // Note: the use of 'Nil' to express "untyped" is not following official documentation and could be improved.

        self.with_param_custom(SignalParam {
            name: parameter_name.into(),
            default: Variant::nil(),
            export_info: ExportInfo::new(VariantType::Nil),
            usage: PropertyUsage::DEFAULT,
        })
    }

    /// Add a parameter for the signal, manually configured.
    #[inline]
    pub fn with_param_custom(mut self, parameter: SignalParam) -> Self {
        self.args.push(parameter);
        self
    }

    /// Finish registering the signal.
    #[inline]
    pub fn done(self) {
        self.class_builder.add_signal(Signal {
            name: self.name,
            args: self.args,
        });
    }
}

pub(crate) struct Signal {
    pub name: GodotString,
    pub args: Vec<SignalParam>,
}

/// Parameter in a signal declaration.
///
/// Instead of providing values for each field, check out the `param*()` methods in [`SignalBuilder`].
pub struct SignalParam {
    /// Parameter name.
    pub name: GodotString,

    /// Default value, used when no argument is provided.
    pub default: Variant,

    /// Metadata and UI hints about exporting, e.g. parameter type.
    pub export_info: ExportInfo,

    /// In which context the signal parameter is used.
    pub usage: PropertyUsage,
}