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}