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
macro_rules! define_message_enum {
    (empty_string=$t:tt) => {""};
    (empty=$t:tt) => {};
    (dotdot=$t:tt) => {..};

    (
        $(#[$attr:meta])*
        $vis:vis enum $ident:ident {
            $(
            $(#[$message_attr:meta])*
            %[$message_string:literal]
            $(%$has_parameters:tt[parameters = [$($(*$has_value:tt)?($message_parameter_ident:ident, $message_parameter_string:literal)),+]])?
            $message_ident:ident
            $(($has_arguments:tt $($message_argument:ty),+))?
            ),+
        }
    ) => {
        $(#[$attr])*
        #[derive(Debug, Clone)]
        $vis enum $ident {
            $(
            $(#[$message_attr])*
            $message_ident
            $(($($message_argument),+))?
            ),+
        }

        ::paste::paste! {
            impl $crate::traits::Message for $ident {
                type MessagePointer = [< $ident Pointer >];
                type MessageParameterPointer = [< $ident ParameterPointer >];
                fn pointer(&self) -> Self::MessagePointer {
                    match self {
                        $(
                        Self::$message_ident$((define_message_enum!(dotdot=$has_arguments)))? => [< $ident Pointer >]::$message_ident
                        ),+
                    }
                }
            }

            #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
            $vis enum [< $ident Pointer >] {
                $(
                $(#[$message_attr])*
                $message_ident
                ),+
            }

            #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
            $vis enum [< $ident ParameterPointer >] {
				$($(
                #[allow(unused_doc_comments)]
                #[doc = define_message_enum!(empty_string=$has_parameters)]
				[< $message_ident:camel >]( [< $ident $message_ident:camel ParameterPointer >]),
				)?)+
			}

			$($(
			#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
            $vis enum [< $ident $message_ident ParameterPointer >] {
				$(
				$message_parameter_ident
				),+
			}
			)?)+

            impl ::std::str::FromStr for [< $ident Pointer >] {
                type Err = ();

                /// Parses a string to a message pointer.
                ///
                /// The error type is blank because there is only one error case: the string simply didn't match a message.
                fn from_str(s: &str) -> Result<Self, Self::Err> {
                    match s {
                        $($message_string => Ok(Self::$message_ident),)+
                        _ => Err(())
                    }
                }
            }

            impl $crate::traits::MessageParameterPointer for [< $ident ParameterPointer >] {
                type MessagePointer = [< $ident Pointer >];

                fn as_string(self) -> &'static str {
                    match self {
                        $($(
                        #[allow(unused_doc_comments)]
                        #[doc = define_message_enum!(empty_string=$has_parameters)]
                        Self::$message_ident(parameter_pointer) => match parameter_pointer {
                            $(
                            [< $ident $message_ident ParameterPointer >]::$message_parameter_ident => $message_parameter_string,
                            )+
                        },
                        )?)+
                    }
                }

                fn from_message_and_str(message_pointer: Self::MessagePointer, s: &str) -> Result<Self, $crate::MessageParameterPointerParseError> {
                    match message_pointer {
                        $($(
                        #[allow(unused_doc_comments)]
                        #[doc = define_message_enum!(empty_string=$has_parameters)]
                        Self::MessagePointer::$message_ident => match s {
                            $(
                            $message_parameter_string => Ok(Self::[< $message_ident >]([< $ident $message_ident ParameterPointer >]::$message_parameter_ident)),
                            )+
                            _ => Err($crate::MessageParameterPointerParseError::StringDoesNotMapToParameterPointer)
                        },
                        )?)+
                        _ => Err($crate::MessageParameterPointerParseError::MessageHasNoParameters)
                    }
                }

                #[allow(unreachable_patterns)]
                fn has_value(self) -> bool {
                    match self {
                        $($(
                        #[allow(unused_doc_comments)]
                        #[doc = define_message_enum!(empty_string=$has_parameters)]
                        Self::$message_ident(parameter_pointer) => match parameter_pointer {
                            $($(
                            #[allow(unused_doc_comments)]
                            #[doc = define_message_enum!(empty_string=$has_value)]
                            [< $ident $message_ident ParameterPointer >]::$message_parameter_ident => false,
                            )?)+
                            _ => true
                        },
                        )?)+
                        _ => true
                    }
                }
            }

            impl $crate::traits::MessagePointer for [< $ident Pointer >] {
                fn as_string(self) -> &'static str {
                    match self {
                        $(Self::$message_ident => $message_string),+
                    }
                }

                fn has_parameters(self) -> bool {
                    match self {
                        $($(
                        #[allow(unused_doc_comments)]
                        #[doc = define_message_enum!(empty_string=$has_parameters)]
                        Self::$message_ident => true,
                        )?)+
                        _ => false
                    }
                }
            }
        }
    };
}

pub(crate) use define_message_enum;