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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#![no_std]

#[doc(hidden)]
#[macro_export]
macro_rules! extension_trait_internal {
    (@finish_parsing [$($pub_token:tt)*]
        $(#[$attr:meta])*
        $(<$(
            $impl_gen_name:tt $(: [$($impl_gen_bound:tt)*])*
        ),*>)*
        $trait_name:ident
        $(<$(
            $trait_gen_name:tt $(: [$($trait_gen_bound:tt)*])*
        ),*>)*
        for $type_name:ty
        $(
            where $(
                $where_impl_gen_name:tt $(: [$($where_impl_gen_bound:tt)*])*
            ),*
            $(,)*
        )*
        { $(
            $(#[$fn_attr:meta])*
            [$($fn_keywords:tt)*] fn $fn_name:ident
            $( < $(
                $fn_gen_name:tt $(: [$($fn_gen_bound:tt)*])*
            ),* > )*
            ( $($args:tt)* ) $(-> $out:ty)*
            $(
                where $(
                    $where_gen_name:ty : [$($where_gen_bound:tt)*]
                ),*
                $(,)*
            )*
            $code:block
        )* }
    ) => {
        $(#[$attr])*
        $($pub_token)*
        trait $trait_name
        $(<$(
            $trait_gen_name $(: [$($trait_gen_bound)*])*
        ),*>)*
        { $(
            // Will be removed once Rust allows `:` to follow `pat` in macros.
            #[allow(patterns_in_fns_without_body)]
            $(#[$fn_attr])*
            $($fn_keywords)* fn $fn_name
            $( < $(
                $fn_gen_name $(: $($fn_gen_bound)*)*
            ),* > )*
            ( $($args)* ) $(-> $out)*
            $( where $( $where_gen_name: $($where_gen_bound)* ),* )*;
        )* }

        impl
        $(<$(
            $impl_gen_name $(: $($impl_gen_bound)*)*
        ),*>)*
        $trait_name
        $(<$(
            $trait_gen_name $(: [$($trait_gen_bound)*])*
        ),*>)*
        for $type_name
        $(
            where
            $(
                $where_impl_gen_name $(: $($where_impl_gen_bound)*)*
            ),*
        )*
        { $(
            $($fn_keywords)* fn $fn_name
            $( < $(
                $fn_gen_name $(: $($fn_gen_bound)*)*
            ),* > )*
            ( $($args)* ) $(-> $out)*
            $( where $( $where_gen_name: $($where_gen_bound)* ),* )*
            $code
        )* }
    };

    (@normalize_block $parsed:tt $block_parsed:tt $pub_token:tt : $($rest:tt)*) => {
        extension_trait_internal!(
            @parse_type_till_end
            [@normalize_block $parsed]
            $block_parsed
            []
            $pub_token
            []
            $($rest)*
        );
    };

    (@normalize_block $parsed:tt {$($block_parsed:tt)*} $pub_token:tt fn $($rest:tt)*) => {
        extension_trait_internal!(
            @normalize_block $parsed {$($block_parsed)* [] fn}
            $pub_token $($rest)*
        );
    };

    (@normalize_block $parsed:tt {$($block_parsed:tt)*} $pub_token:tt unsafe fn $($rest:tt)*) => {
        extension_trait_internal!(
            @normalize_block $parsed {$($block_parsed)* [unsafe] fn}
            $pub_token $($rest)*
        );
    };

    (@normalize_block {$($parsed:tt)*} $block_parsed:tt $pub_token:tt) => {
        extension_trait_internal!(@finish_parsing $pub_token $($parsed)* $block_parsed);
    };

    (
        @normalize_block $parsed:tt {$($block_parsed:tt)*}
        $pub_token:tt $parsed_token:tt $($rest:tt)*
    ) => {
        extension_trait_internal!(
            @normalize_block $parsed {$($block_parsed)* $parsed_token}
            $pub_token $($rest)*
        );
    };

    (@normalize_expression $parsed:tt $pub_token:tt {$($contents:tt)*}) => {
        extension_trait_internal!(@normalize_block $parsed {} $pub_token $($contents)*);
    };

    (@normalize_expression $parsed:tt [] pub $($rest:tt)*) => {
        extension_trait_internal!(@normalize_expression $parsed [pub] $($rest)*);
    };

    (@normalize_expression $parsed:tt $pub_token:tt : $($rest:tt)*) => {
        extension_trait_internal!(
            @parse_type_till_end [@normalize_expression] $parsed []
            $pub_token [] $($rest)*
        );
    };

    (
        @parse_type_till_end $return_trait:tt $parsed:tt [$($left_brackets:tt)*]
        $pub_token:tt [$($type_name:tt)*] < $($rest:tt)*
    ) => {
        extension_trait_internal!(
            @parse_type_till_end $return_trait $parsed [$($left_brackets)* <]
            $pub_token [$($type_name)* <] $($rest)*
        );
    };

    (
        @parse_type_till_end $return_trait:tt $parsed:tt $left_brackets:tt
        $pub_token:tt $type_name:tt >> $($rest:tt)*
    ) => {
        extension_trait_internal!(
            @parse_type_till_end $return_trait $parsed $left_brackets
            $pub_token $type_name > > $($rest)*
        );
    };

    (
        @parse_type_till_end [$($return_trait:tt)*] {$($parsed:tt)*} []
        $pub_token:tt $type_name:tt > $($rest:tt)*
    ) => {
        extension_trait_internal!(
            $($return_trait)* {$($parsed)*: $type_name>}
            $pub_token $($rest)*
        );
    };

    (
        @parse_type_till_end [$($return_trait:tt)*] {$($parsed:tt)*} $left_brackets:tt
        $pub_token:tt $type_name:tt , $($rest:tt)*
    ) => {
        extension_trait_internal!(
            $($return_trait)* {$($parsed)*: $type_name,}
            $pub_token $($rest)*
        );
    };

    (
        @parse_type_till_end [$($return_trait:tt)*] {$($parsed:tt)*} []
        $pub_token:tt $type_name:tt {$($block:tt)*} $($rest:tt)*
    ) => {
        extension_trait_internal!(
            $($return_trait)* {$($parsed)*: $type_name}
            $pub_token {$($block)*} $($rest)*
        );
    };

    (
        @parse_type_till_end $return_trait:tt $parsed:tt [< $($left_brackets:tt)*]
        $pub_token:tt [$($type_name:tt)*] > $($rest:tt)*
    ) => {
        extension_trait_internal!(@parse_type_till_end $return_trait $parsed
        [$($left_brackets)*] $pub_token [$($type_name)* >] $($rest)*);
    };

    (
        @parse_type_till_end $return_trait:tt $parsed:tt $left_brackets:tt
        $pub_token:tt [$($type_name:tt)*] $token:tt $($rest:tt)*
    ) => {
        extension_trait_internal!(
            @parse_type_till_end
            $return_trait
            $parsed
            $left_brackets
            $pub_token
            [$($type_name)* $token]
            $($rest)*
        );
    };

    (@normalize_expression {$($parsed:tt)*} $pub_token:tt $parsed_token:tt $($rest:tt)*) => {
        extension_trait_internal!(
            @normalize_expression {$($parsed)* $parsed_token}
            $pub_token $($rest)*
        );
    };
}

/// Declares an extension trait
///
/// # Example
///
/// ```
/// #[macro_use]
/// extern crate extension_trait;
///
/// extension_trait! { pub DoubleExt for str {
///    fn double(&self) -> String {
///        self.repeat(2)
///    }
/// } }
///
/// fn main() {
///     assert_eq!("Hello".double(), "HelloHello");
/// }
/// ```
#[macro_export]
macro_rules! extension_trait {
    ($($token:tt)+) => {
        extension_trait_internal!(@normalize_expression {} [] $($token)+);
    };
}