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
//! Contains code common to telety and telety-macro.
//! Only items re-exported through telety should be considered public.

pub mod alias;
pub use alias::Alias;
mod command;
pub use command::Command;
mod item_data;
pub mod find_and_replace;
mod options;
pub use options::Options;
pub mod syn_util;
mod telety;
pub use telety::Telety;
pub mod version;
pub mod visitor;

/// Accept any arguments and output nothing.
/// ## Example
/// ```rust
/// # use telety_impl::noop;
/// let mut a = true;
/// noop!(a = false);
/// assert!(a);
/// ```
#[macro_export]
macro_rules! noop {
    ($($tokens:tt)*) => {};
}

/// Unconditional compile error. For use with `macro_fallback` to generate
/// a clearer error message.
/// ## Example
/// ```rust,compile_fail
/// # use telety_impl::no_telety_error;
/// no_telety_error!(nothing stops the error);
/// ```
#[macro_export]
macro_rules! no_telety_error {
    ($($tokens:tt)*) => {
        compile_error!("Type does not have a telety macro");
    };
}

/// Call a macro with the given arguments.
/// If the first path does not contain a macro, use the macro at the second path instead.   
/// ## Limitations
/// * The first path must be valid for *some* kind of object (i.e. a type, value, or macro).
/// * The first path must have a path qualifier (start with `::`, `crate`, `self`, or `super`).
///   Otherwise, name resolution will fail.  
///   ("cannot determine resolution for the macro `__macro_fallback` import resolution is stuck,
///   try simplifying macro imports")
/// * The macro is expanded within a block, so any items generated will not have a canonical name.
///   Generating `impl`s is the primary use case.
/// ## Example
/// ```rust,ignore
/// # use telety_impl::{macro_fallback, noop};
/// macro_rules! AMacro {
///     ($($tokens:tt)*) => { $($tokens)* };
/// }
/// use AMacro as AMacro;
///
/// let mut a = false;
/// macro_fallback!(self::AMacro, telety::noop, a = true;);
/// assert!(a);
///
/// struct NotAMacro;
/// macro_fallback!(self::NotAMacro, telety::noop, unreachable!());
/// ```
#[macro_export]
macro_rules! macro_fallback {
    ($maybe:path, $fallback:path, $($tokens:tt)*) => {
        const _: () = {
            #[allow(unused_imports)]
            use $fallback as __macro_fallback;
            const _: () = {
                #[allow(unused_imports)]
                use $maybe as __macro_fallback;
                __macro_fallback! {
                    $($tokens)*
                }
            };
        };
    }
}

#[cfg(test)]
mod test {
    struct _YesMacro;
    macro_rules! _YesMacro {
        ($t:ty) => {
            impl $t {
                pub const fn text() -> &'static str {
                    "YesMacro"
                }
            }
        };
    }
    use _YesMacro as YesMacro;

    struct NoMacro;

    #[test]
    fn macro_fallback() {
        macro_rules! _MacroFallback {
            ($t:ty) => {
                impl $t {
                    pub const fn text() -> &'static str {
                        "Fallback"
                    }
                }
            };
        }
        use _MacroFallback as MacroFallback;

        macro_fallback!(self::YesMacro, MacroFallback, self::YesMacro);
        macro_fallback!(self::NoMacro, MacroFallback, self::NoMacro);

        assert_ne!(self::YesMacro::text(), "Fallback");
        assert_eq!(self::NoMacro::text(), "Fallback");
    }
}