xref: /DragonOS/kernel/crates/intertrait/macros/src/lib.rs (revision c635d8a9cfe25bc11779f323ef0c7d7a0f597d4a)
1 extern crate proc_macro;
2 
3 use proc_macro::TokenStream;
4 
5 use syn::{parse, parse_macro_input, DeriveInput, ItemImpl};
6 
7 use args::{Casts, Flag, Targets};
8 use gen_caster::generate_caster;
9 
10 mod args;
11 mod gen_caster;
12 mod item_impl;
13 mod item_type;
14 
15 /// Attached on an `impl` item or type definition, registers traits as targets for casting.
16 ///
17 /// If on an `impl` item, no argument is allowed. But on a type definition, the target traits
18 /// must be listed explicitly.
19 ///
20 /// Add `[sync]` before the list of traits if the underlying type is `Sync + Send` and you
21 /// need `std::sync::Arc`.
22 ///
23 /// # Examples
24 /// ## On a trait impl
25 /// ```
26 /// use intertrait::*;
27 ///
28 /// struct Data;
29 ///
30 /// trait Greet {
31 ///     fn greet(&self);
32 /// }
33 ///
34 /// // Greet can be cast into from any sub-trait of CastFrom implemented by Data.
35 /// #[cast_to]
36 /// impl Greet for Data {
37 ///     fn greet(&self) {
38 ///         println!("Hello");
39 ///     }
40 /// }
41 /// ```
42 ///
43 /// ## On a type definition
44 /// Use when a target trait is derived or implemented in an external crate.
45 /// ```
46 /// use intertrait::*;
47 ///
48 /// // Debug can be cast into from any sub-trait of CastFrom implemented by Data
49 /// #[cast_to(std::fmt::Debug)]
50 /// #[derive(std::fmt::Debug)]
51 /// struct Data;
52 /// ```
53 ///
54 /// ## For Arc
55 /// Use when the underlying type is `Sync + Send` and you want to use `Arc`.
56 /// ```
57 /// use intertrait::*;
58 ///
59 /// // Debug can be cast into from any sub-trait of CastFrom implemented by Data
60 /// #[cast_to([sync] std::fmt::Debug)]
61 /// #[derive(std::fmt::Debug)]
62 /// struct Data;
63 /// ```
64 
65 #[proc_macro_attribute]
66 pub fn cast_to(args: TokenStream, input: TokenStream) -> TokenStream {
67     match parse::<Targets>(args) {
68         Ok(Targets { flags, paths }) => {
69             if paths.is_empty() {
70                 item_impl::process(&flags, parse_macro_input!(input as ItemImpl))
71             } else {
72                 item_type::process(&flags, paths, parse_macro_input!(input as DeriveInput))
73             }
74         }
75         Err(err) => vec![err.to_compile_error(), input.into()]
76             .into_iter()
77             .collect(),
78     }
79     .into()
80 }
81 
82 /// Declares target traits for casting implemented by a type.
83 ///
84 /// This macro is for registering both a concrete type and its traits to be targets for casting.
85 /// Useful when the type definition and the trait implementations are in an external crate.
86 ///
87 /// **Note**: this macro cannot be used in an expression or statement prior to Rust 1.45.0,
88 /// due to [a previous limitation](https://github.com/rust-lang/rust/pull/68717).
89 /// If you want to use it in an expression or statement, use Rust 1.45.0 or later.
90 ///
91 /// # Examples
92 /// ```
93 /// use intertrait::*;
94 ///
95 /// #[derive(std::fmt::Debug)]
96 /// enum Data {
97 ///     A, B, C
98 /// }
99 /// trait Greet {
100 ///     fn greet(&self);
101 /// }
102 /// impl Greet for Data {
103 ///     fn greet(&self) {
104 ///         println!("Hello");
105 ///     }
106 /// }
107 ///
108 /// castable_to! { Data => std::fmt::Debug, Greet }
109 ///
110 /// # fn main() {}
111 /// ```
112 ///
113 /// When the type is `Sync + Send` and is used with `Arc`:
114 /// ```
115 /// use intertrait::*;
116 ///
117 /// #[derive(std::fmt::Debug)]
118 /// enum Data {
119 ///     A, B, C
120 /// }
121 /// trait Greet {
122 ///     fn greet(&self);
123 /// }
124 /// impl Greet for Data {
125 ///     fn greet(&self) {
126 ///         println!("Hello");
127 ///     }
128 /// }
129 /// castable_to! { Data => [sync] std::fmt::Debug, Greet }
130 ///
131 /// # fn main() {}
132 /// ```
133 #[proc_macro]
134 pub fn castable_to(input: TokenStream) -> TokenStream {
135     let Casts {
136         ty,
137         targets: Targets { flags, paths },
138     } = parse_macro_input!(input);
139 
140     paths
141         .iter()
142         .map(|t| generate_caster(&ty, t, flags.contains(&Flag::Sync)))
143         .collect::<proc_macro2::TokenStream>()
144         .into()
145 }
146