Skip to main content

television_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3
4/// This macro generates the `OnAir` trait implementation for the given enum.
5///
6/// The `OnAir` trait is used to interact with the different television channels
7/// and forwards the method calls to the corresponding channel variants.
8///
9/// Example:
10/// ```ignore
11/// use television-derive::Broadcast;
12/// use television::channels::{TelevisionChannel, OnAir};
13/// use television::channels::{files, text};
14///
15/// #[derive(Broadcast)]
16/// enum TelevisionChannel {
17///     Files(files::Channel),
18///     Text(text::Channel),
19/// }
20///
21/// let mut channel = TelevisionChannel::Files(files::Channel::default());
22///
23/// // Use the `OnAir` trait methods directly on TelevisionChannel
24/// channel.find("pattern");
25/// let results = channel.results(10, 0);
26/// let result = channel.get_result(0);
27/// let result_count = channel.result_count();
28/// let total_count = channel.total_count();
29/// let running = channel.running();
30/// channel.shutdown();
31/// ```
32#[proc_macro_derive(Broadcast)]
33pub fn tv_channel_derive(input: TokenStream) -> TokenStream {
34    // Construct a representation of Rust code as a syntax tree
35    // that we can manipulate
36    let ast = syn::parse(input).unwrap();
37
38    // Build the trait implementation
39    impl_tv_channel(&ast)
40}
41
42fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
43    // Ensure the struct is an enum
44    let variants = if let syn::Data::Enum(data_enum) = &ast.data {
45        &data_enum.variants
46    } else {
47        panic!("#[derive(OnAir)] is only defined for enums");
48    };
49
50    // Ensure the enum has at least one variant
51    assert!(
52        !variants.is_empty(),
53        "#[derive(OnAir)] requires at least one variant"
54    );
55
56    let enum_name = &ast.ident;
57
58    let variant_names: Vec<_> = variants.iter().map(|v| &v.ident).collect();
59
60    // Generate the trait implementation for the TelevisionChannel trait
61    let trait_impl = quote! {
62        impl OnAir for #enum_name {
63            fn find(&mut self, pattern: &str) {
64                match self {
65                    #(
66                        #enum_name::#variant_names(ref mut channel) => {
67                            channel.find(pattern);
68                        }
69                    )*
70                }
71            }
72
73            fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
74                match self {
75                    #(
76                        #enum_name::#variant_names(ref mut channel) => {
77                            channel.results(num_entries, offset)
78                        }
79                    )*
80                }
81            }
82
83            fn get_result(&self, index: u32) -> Option<Entry> {
84                match self {
85                    #(
86                        #enum_name::#variant_names(ref channel) => {
87                            channel.get_result(index)
88                        }
89                    )*
90                }
91            }
92
93            fn selected_entries(&self) -> &FxHashSet<Entry> {
94                match self {
95                    #(
96                        #enum_name::#variant_names(ref channel) => {
97                            channel.selected_entries()
98                        }
99                    )*
100                }
101            }
102
103            fn toggle_selection(&mut self, entry: &Entry) {
104                match self {
105                    #(
106                        #enum_name::#variant_names(ref mut channel) => {
107                            channel.toggle_selection(entry)
108                        }
109                    )*
110                }
111            }
112
113            fn result_count(&self) -> u32 {
114                match self {
115                    #(
116                        #enum_name::#variant_names(ref channel) => {
117                            channel.result_count()
118                        }
119                    )*
120                }
121            }
122
123            fn total_count(&self) -> u32 {
124                match self {
125                    #(
126                        #enum_name::#variant_names(ref channel) => {
127                            channel.total_count()
128                        }
129                    )*
130                }
131            }
132
133            fn running(&self) -> bool {
134                match self {
135                    #(
136                        #enum_name::#variant_names(ref channel) => {
137                            channel.running()
138                        }
139                    )*
140                }
141            }
142
143            fn shutdown(&self) {
144                match self {
145                    #(
146                        #enum_name::#variant_names(ref channel) => {
147                            channel.shutdown()
148                        }
149                    )*
150                }
151            }
152
153            fn supports_preview(&self) -> bool {
154                match self {
155                    #(
156                        #enum_name::#variant_names(ref channel) => {
157                            channel.supports_preview()
158                        }
159                    )*
160                }
161            }
162        }
163    };
164
165    trait_impl.into()
166}