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
extern crate proc_macro;

use proc_macro::TokenStream;

use syn::{parse, parse_macro_input, DeriveInput, ItemImpl};

use args::{Casts, Flag, Targets};
use gen_caster::generate_caster;

mod args;
mod gen_caster;
mod item_impl;
mod item_type;

/// Attached on an `impl` item or type definition, registers traits as targets for casting.
///
/// If on an `impl` item, no argument is allowed. But on a type definition, the target traits
/// must be listed explicitly.
///
/// Add `[sync]` before the list of traits if the underlying type is `Sync + Send` and you
/// need `std::sync::Arc`.
///
/// # Examples
/// ## On a trait impl
/// ```
/// use intertrait::*;
///
/// struct Data;
///
/// trait Greet {
///     fn greet(&self);
/// }
///
/// // Greet can be cast into from any sub-trait of CastFrom implemented by Data.
/// #[cast_to]
/// impl Greet for Data {
///     fn greet(&self) {
///         println!("Hello");
///     }
/// }
/// ```
///
/// ## On a type definition
/// Use when a target trait is derived or implemented in an external crate.
/// ```
/// use intertrait::*;
///
/// // Debug can be cast into from any sub-trait of CastFrom implemented by Data
/// #[cast_to(std::fmt::Debug)]
/// #[derive(std::fmt::Debug)]
/// struct Data;
/// ```
///
/// ## For Arc
/// Use when the underlying type is `Sync + Send` and you want to use `Arc`.
/// ```
/// use intertrait::*;
///
/// // Debug can be cast into from any sub-trait of CastFrom implemented by Data
/// #[cast_to([sync] std::fmt::Debug)]
/// #[derive(std::fmt::Debug)]
/// struct Data;
/// ```
#[proc_macro_attribute]
pub fn cast_to(args: TokenStream, input: TokenStream) -> TokenStream {
    match parse::<Targets>(args) {
        Ok(Targets { flags, paths }) => {
            if paths.is_empty() {
                item_impl::process(&flags, parse_macro_input!(input as ItemImpl))
            } else {
                item_type::process(&flags, paths, parse_macro_input!(input as DeriveInput))
            }
        }
        Err(err) => vec![err.to_compile_error(), input.into()]
            .into_iter()
            .collect(),
    }
    .into()
}

/// Declares target traits for casting implemented by a type.
///
/// This macro is for registering both a concrete type and its traits to be targets for casting.
/// Useful when the type definition and the trait implementations are in an external crate.
///
/// **Note**: this macro cannot be used in an expression or statement prior to Rust 1.45.0,
/// due to [a previous limitation](https://github.com/rust-lang/rust/pull/68717).
/// If you want to use it in an expression or statement, use Rust 1.45.0 or later.
///
/// # Examples
/// ```
/// use intertrait::*;
///
/// #[derive(std::fmt::Debug)]
/// enum Data {
///     A, B, C
/// }
/// trait Greet {
///     fn greet(&self);
/// }
/// impl Greet for Data {
///     fn greet(&self) {
///         println!("Hello");
///     }
/// }
///
/// castable_to! { Data => std::fmt::Debug, Greet }
///
/// # fn main() {}
/// ```
///
/// When the type is `Sync + Send` and is used with `Arc`:
/// ```
/// use intertrait::*;
///
/// #[derive(std::fmt::Debug)]
/// enum Data {
///     A, B, C
/// }
/// trait Greet {
///     fn greet(&self);
/// }
/// impl Greet for Data {
///     fn greet(&self) {
///         println!("Hello");
///     }
/// }
/// castable_to! { Data => [sync] std::fmt::Debug, Greet }
///
/// # fn main() {}
/// ```
#[proc_macro]
pub fn castable_to(input: TokenStream) -> TokenStream {
    let Casts {
        ty,
        targets: Targets { flags, paths },
    } = parse_macro_input!(input);

    paths
        .iter()
        .map(|t| generate_caster(&ty, t, flags.contains(&Flag::Sync)))
        .collect::<proc_macro2::TokenStream>()
        .into()
}