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
#![feature(external_doc)] #![doc(include = "../doc/README.md")] use quote::quote; use proc_macro_hack::proc_macro_hack; use syn::{Token, Type}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; struct CommaPunctuatedTyTokenStream { len :usize, } impl From<CommaPunctuatedTyTokenStream> for usize { fn from(comma_punctuated_ty_token_stream: CommaPunctuatedTyTokenStream) -> Self { comma_punctuated_ty_token_stream.len } } impl Parse for CommaPunctuatedTyTokenStream { fn parse(parse_stream :ParseStream) -> syn::Result<Self> { let parse = Punctuated::<Type, Token![,]>::parse_terminated; let result = parse(parse_stream)?; Ok(CommaPunctuatedTyTokenStream{len : result.len()}) } } // TODO: Switch out from proc_macro_hack and edit documentation when // "error: procedural macros cannot be expanded to expressions" is resloved. // note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information #[proc_macro_hack] /// Returns the count of comma-delimited [`:ty`]s (types) in the given [`TokenStream`] /// as a constant expression of type usize /// /// # Arguments /// /// * `input` - A [`TokenStream`] in which comma-delimited [`:ty`]s (types) must be counted /// /// # Examples /// /// ## Basic usage: /// ```rust /// // count_tys!($($ty:ty),*) /// ``` /// /// ## More complete example: /// /// ### Cargo.toml dependencies /// /// ```rust /// /* /// [dependencies] /// proc-macro-hack = "0.5" /// count-tys = "0.1" /// */ /// ``` /// /// ### main.rs /// /// ```rust /// extern crate proc_macro_hack; /// use proc_macro_hack::proc_macro_hack; /// #[proc_macro_hack] /// use count_tts::count_tys; /// /// // It not necessarily must be a struct, it could be a generic /// // Read more about macro_rules! here: /// // <https://doc.rust-lang.org/rust-by-example/macros.html> /// macro_rules! declare_variadic_struct { /// ($struct_name:ident, <$($ty:ty),*>) => { /// struct $struct_name { /// // fields /// } /// /// impl $struct_name { /// pub const fn count() -> usize { /// // count_tys!() can be used in an expression and even /// // const expression context /// // unlike macros without proc_macro_hack /// // note: see issue #54727 /// // <https://github.com/rust-lang/rust/issues/54727> /// // for more information. /// count_tys!($($ty:ty),*) /// } /// } /// }; /// } /// /// // declare_variadic_struct!(VariadicStruct, <usize, usize, usize>); /// // expands into the following: /// // /// // struct VariadicStruct { /// // // fields /// // } /// // /// // impl VariadicStuct { /// // pub const fn count() -> usize { /// // 3usize /// // } /// // } /// declare_variadic_struct!(VariadicStruct, <usize, usize, usize>); /// assert_eq!(VariadicStruct::count(), 3); /// ``` /// /// [`usize`]: https://doc.rust-lang.org/std/primitive.usize.html /// [`TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html /// [`TokenTree`]: https://doc.rust-lang.org/proc_macro/enum.TokenTree.html /// [`:ty`]: https://doc.rust-lang.org/rust-by-example/macros/designators.html pub fn count_tys(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Unlike the following macro by example when applied to $($tys:ty),* [`pattern`], // // ``` // // https://danielkeep.github.io/tlborm/book/blk-counting.html // macro_rules! count_tts { // () => {0usize}; // ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)}; // } // ``` // // the following implementation does not produce heavy [`TokenStream`] of "1usize + 1usize...". // // [`TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html // [`TokenTree`]: https://doc.rust-lang.org/proc_macro/enum.TokenTree.html // [`designator`]: https://doc.rust-lang.org/rust-by-example/macros/designators.html // [`pattern`]: https://doc.rust-lang.org/rust-by-example/macros/designators.html // let comma_punctuated_ty_token_stream = syn::parse_macro_input!(input as CommaPunctuatedTyTokenStream); let tys_count :usize = comma_punctuated_ty_token_stream.into(); let expanded_tt = quote!{#tys_count}; proc_macro::TokenStream::from(expanded_tt) }