Skip to main content

gmt_dos_clients_scopehub/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Ident, ItemEnum, Type, parse_macro_input};
4
5#[proc_macro_attribute]
6pub fn scopehub(_args: TokenStream, input: TokenStream) -> TokenStream {
7    let input = parse_macro_input!(input as ItemEnum);
8
9    let hub = input.ident;
10    let hub_error = format_ident!("{hub}Error");
11
12    let (scope_ty, signal_ty): (Vec<_>, Vec<_>) = input
13        .variants
14        .iter()
15        .flat_map(|v| {
16            v.fields
17                .iter()
18                .map(|field| (v.ident.clone(), field.ty.clone()))
19                .collect::<Vec<(Ident, Type)>>()
20        })
21        .unzip();
22
23    let idents: Vec<_> = scope_ty
24        .iter()
25        .enumerate()
26        // .map(|field| field.to_string().to_lowercase())
27        .map(|(i, _)| format_ident!("scope_{i}"))
28        .collect();
29
30    // Build the output, possibly using quasi-quotation
31    let scope_hub_server = quote! {
32        #[derive(Debug)]
33        pub enum #hub_error{
34            Server(::gmt_dos_clients_scope::server::ServerError)
35        }
36        impl ::std::fmt::Display for #hub_error{
37            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
38                write!(f, "failed to initiate scopes hub")
39            }
40        }
41        impl ::std::error::Error for #hub_error{
42            fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
43                match self {
44                    Self::Server(source) => Some(source),
45                    _ => None
46                }
47            }
48        }
49        impl From<::gmt_dos_clients_scope::server::ServerError> for #hub_error{
50            fn from(e: ::gmt_dos_clients_scope::server::ServerError) -> Self {
51                Self::Server(e)
52            }
53        }
54        /// Scopes hub
55        pub struct #hub {
56            monitor: Option<::gmt_dos_clients_scope::server::Monitor>,
57            #(#idents: ::gmt_dos_clients_scope::server::#scope_ty<#signal_ty>),*
58        }
59        impl #hub {
60            /// Creates a new scopes hub instance
61            pub fn new() -> Result<Self,#hub_error> {
62                let mut monitor = ::gmt_dos_clients_scope::server::Monitor::new();
63                #(let #idents = ::gmt_dos_clients_scope::server::#scope_ty::<#signal_ty>::builder(&mut monitor).build()?;)*
64                Ok(Self {
65                    monitor: Some(monitor),
66                    #(#idents),*
67                })
68            }
69            /// Closes the scopes hub
70            pub async fn close(&mut self) -> Result<(),#hub_error> {
71                #(self.#idents.end_transmission();)*
72                if let Some(monitor) = self.monitor.take() {
73                    monitor.join().await.map_err(|e| ::gmt_dos_clients_scope::server::ServerError::Transmitter(e))?;
74                }
75                Ok(())
76            }
77        }
78        impl ::interface::Update for #hub {}
79        #(
80        impl ::interface::Read<#signal_ty> for #hub {
81            fn read(&mut self, data: ::interface::Data<#signal_ty>)  {
82                <_ as ::interface::Read<#signal_ty>>::read(&mut self.#idents, data);
83            }
84        }
85        )*
86        impl ::std::future::IntoFuture for &mut #hub {
87            type Output = <::gmt_dos_clients_scope::server::Monitor as ::std::future::IntoFuture>::Output;
88            type IntoFuture = <::gmt_dos_clients_scope::server::Monitor as ::std::future::IntoFuture>::IntoFuture;
89            fn into_future(self) -> Self::IntoFuture {
90                #(self.#idents.end_transmission();)*
91                self.monitor.take().unwrap().into_future()
92            }
93        }
94    };
95
96    // Hand the output tokens back to the compiler
97    TokenStream::from(scope_hub_server)
98}