1 | use proc_macro2::{Span, TokenStream}; |
2 | use quote::{ToTokens, TokenStreamExt}; |
3 | use syn; |
4 | |
5 | /// Deprecation notes we want to emit to the user, implementing |
6 | /// `quote::ToTokens`. |
7 | /// |
8 | /// Can be expanded at every place that accepts statements and item definitions |
9 | /// (e.g. function bodys). |
10 | /// |
11 | /// # Examples |
12 | /// |
13 | /// Will expand to something like the following (depending on settings): |
14 | /// |
15 | /// ```rust,ignore |
16 | /// # #[macro_use ] |
17 | /// # extern crate quote; |
18 | /// # extern crate derive_builder_core; |
19 | /// # use derive_builder_core::DeprecationNotes; |
20 | /// # fn main() { |
21 | /// # let mut note = DeprecationNotes::default(); |
22 | /// # note.push("Some Warning" .to_string()); |
23 | /// # assert_eq!(quote!(#note).to_string(), quote!( |
24 | /// { |
25 | /// #[deprecated(note = "Some Warning" )] |
26 | /// fn derive_builder_core_deprecation_note() { } |
27 | /// derive_builder_core_deprecation_note(); |
28 | /// } |
29 | /// # ).to_string()); |
30 | /// # } |
31 | /// ``` |
32 | /// |
33 | /// This will emit a deprecation warning in the downstream crate. Cool stuff. ^^ |
34 | /// |
35 | /// Proof of concept: |
36 | /// - <https://play.rust-lang.org/?gist=8394141c07d1f6d75d314818389eb4d8> |
37 | #[derive (Debug, Default, Clone)] |
38 | pub struct DeprecationNotes(Vec<String>); |
39 | |
40 | impl ToTokens for DeprecationNotes { |
41 | fn to_tokens(&self, tokens: &mut TokenStream) { |
42 | for note: &String in &self.0 { |
43 | let fn_ident: Ident = |
44 | syn::Ident::new(string:"derive_builder_core_deprecation_note" , Span::call_site()); |
45 | tokens.append_all(iter:quote!( |
46 | { |
47 | #[deprecated(note=#note)] |
48 | fn #fn_ident() { } |
49 | #fn_ident(); |
50 | } |
51 | )); |
52 | } |
53 | } |
54 | } |
55 | |
56 | impl DeprecationNotes { |
57 | /// Appends a note to the collection. |
58 | #[cfg (test)] |
59 | pub fn push(&mut self, note: String) { |
60 | self.0.push(note) |
61 | } |
62 | |
63 | /// Create a view of these deprecation notes that can annotate a struct. |
64 | pub const fn as_item(&self) -> DeprecationNotesAsItem { |
65 | DeprecationNotesAsItem(self) |
66 | } |
67 | } |
68 | |
69 | /// A view of `DeprecationNotes` that can be used in any context that accept |
70 | /// items. |
71 | /// |
72 | /// Expands to a function `__deprecation_notes` which emits the notes. |
73 | #[derive (Debug)] |
74 | pub struct DeprecationNotesAsItem<'a>(&'a DeprecationNotes); |
75 | |
76 | impl<'a> ToTokens for DeprecationNotesAsItem<'a> { |
77 | fn to_tokens(&self, tokens: &mut TokenStream) { |
78 | let deprecation_notes: &DeprecationNotes = self.0; |
79 | |
80 | if !deprecation_notes.0.is_empty() { |
81 | tokens.append_all(iter:quote!( |
82 | #[doc(hidden)] |
83 | fn derive_builder_core_deprecation_note() { |
84 | #deprecation_notes |
85 | } |
86 | )) |
87 | } |
88 | } |
89 | } |
90 | |
91 | #[test ] |
92 | fn deprecation_note() { |
93 | let mut note: DeprecationNotes = DeprecationNotes::default(); |
94 | note.push("Some Warning" .to_string()); |
95 | assert_eq!( |
96 | quote!(#note).to_string(), |
97 | quote!({ |
98 | #[deprecated(note = "Some Warning" )] |
99 | fn derive_builder_core_deprecation_note() {} |
100 | derive_builder_core_deprecation_note(); |
101 | }) |
102 | .to_string() |
103 | ); |
104 | } |
105 | |