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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Handle messages between scripts or from the engine.
//! See [Message] and [MessageType] for more information.
use serde::{de::DeserializeOwned, Deserialize, Serialize};

/// Represents a message that can be sent between scripts or from the engine.
///
/// # Example
/// ```no_run
/// # use serde::{Deserialize, Serialize};
/// # use lotus_script::prelude::*;
/// #[derive(Serialize, Deserialize)]
/// struct TestMessage {
///     value: i32,
/// }
///
/// impl MessageType for TestMessage {
///     fn id() -> &'static str {
///         "test_message"
///     }
/// }
///
/// fn handle(message: &Message) {
///     message.handle::<TestMessage>(|m| {
///        log::info!("Received message: {}", m.value);
///        Ok(())
///     }).expect("message handle failed");
/// }
/// # handle(&Message::new(TestMessage { value: 42 }));
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
    id: String,
    value: serde_json::Value,
}

/// Represents a message type that can be sent between scripts or from the engine.
/// The [MessageType::id] method should return a globally unique identifier for the message type. If in doubt, use a UUID.
pub trait MessageType: Serialize + DeserializeOwned {
    /// The identifier for the message type.
    fn id() -> &'static str;
}

#[derive(Debug, thiserror::Error)]
pub enum MessageValueError {
    #[error("invalid message type")]
    InvalidType,
    #[error("{0}")]
    Serialization(SerializationError),
}

#[derive(Debug, thiserror::Error)]
#[error("serialization error: {0}")]
pub struct SerializationError(String);

#[derive(Debug, thiserror::Error)]
pub enum MessageHandleError {
    #[error("{0}")]
    Serialization(SerializationError),
    #[error("handler error: {0}")]
    Handler(Box<dyn std::error::Error>),
}

impl Message {
    /// Creates a new message with the given value.
    pub fn new<T: MessageType>(value: T) -> Self {
        Self {
            id: T::id().to_string(),
            value: serde_json::to_value(value).unwrap(),
        }
    }

    /// Returns the message type ID.
    pub fn id(&self) -> &str {
        &self.id
    }

    /// Returns the message value as the given type. Returns a [MessageValueError] if the message has a different type.
    pub fn value<T: MessageType>(&self) -> Result<T, MessageValueError> {
        if self.id != T::id() {
            return Err(MessageValueError::InvalidType);
        }

        serde_json::from_value(self.value.clone())
            .map_err(|e| MessageValueError::Serialization(SerializationError(e.to_string())))
    }

    /// Returns `true` if the message has the given type.
    pub fn has_type<T: MessageType>(&self) -> bool {
        self.id == T::id()
    }

    /// Handle the message with the given handler function.
    /// Returns `Ok(true)` if the message was handled, `Ok(false)` if the message has a different type,
    /// or `Err` if the message could not be deserialized or the handler function returned an error.
    /// The handler function should return `Ok(())` if the message was handled successfully.
    pub fn handle<T: MessageType>(
        &self,
        f: impl FnOnce(T) -> Result<(), Box<dyn std::error::Error>>,
    ) -> Result<bool, MessageHandleError> {
        match self.value::<T>() {
            Ok(v) => f(v)
                .map_err(|e| MessageHandleError::Handler(e))
                .map(|_| true),
            Err(MessageValueError::InvalidType) => Ok(false),
            Err(MessageValueError::Serialization(e)) => Err(MessageHandleError::Serialization(e)),
        }
    }

    /// Sends the message to the given target.
    #[cfg(feature = "ffi")]
    pub fn send(&self, target: MessageTarget) {
        let this = lotus_script_sys::FfiObject::new(self);
        let target = lotus_script_sys::FfiObject::new(&target);

        unsafe { lotus_script_sys::messages::send(target.packed(), this.packed()) }
    }
}

/// Represents a message target.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageTarget {
    Myself,
    ChildByIndex(usize),
    Broadcast,
    BroadcastExceptSelf,
    Parent,
}

#[cfg(test)]
mod tests {
    use serde::Deserialize;

    use super::*;

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    struct TestMessage {
        value: i32,
    }

    impl MessageType for TestMessage {
        fn id() -> &'static str {
            "test_message"
        }
    }

    #[test]
    fn test_message() {
        let message = Message::new(TestMessage { value: 42 });
        assert_eq!(message.id(), "test_message");

        let value = message.value::<TestMessage>().unwrap();

        assert_eq!(value, TestMessage { value: 42 });

        message
            .handle::<TestMessage>(|m| {
                assert_eq!(m.value, 42);
                Ok(())
            })
            .expect("message handle failed");
    }
}