use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Index, LitStr};
#[proc_macro_derive(MediaSink, attributes(sink))]
pub fn derive_media_sink(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let (impl_g, ty_g, where_c) = input.generics.split_for_impl();
let fields = match &input.data {
Data::Struct(s) => &s.fields,
_ => {
return syn::Error::new_spanned(name, "MediaSink can only be derived for structs")
.to_compile_error()
.into()
}
};
let accessor = match pick_delegate(fields) {
Ok(tokens) => tokens,
Err(e) => return e.to_compile_error().into(),
};
quote! {
#[::arcly_stream::__macro_support::async_trait]
impl #impl_g ::arcly_stream::traits::MediaSink for #name #ty_g #where_c {
async fn send_frame(
&mut self,
frame: ::arcly_stream::MediaFrame,
) -> ::arcly_stream::Result<()> {
::arcly_stream::traits::MediaSink::send_frame(&mut self.#accessor, frame).await
}
async fn flush(&mut self) -> ::arcly_stream::Result<()> {
::arcly_stream::traits::MediaSink::flush(&mut self.#accessor).await
}
}
}
.into()
}
fn pick_delegate(fields: &Fields) -> syn::Result<proc_macro2::TokenStream> {
let marked = |attrs: &[syn::Attribute]| attrs.iter().any(|a| a.path().is_ident("sink"));
match fields {
Fields::Named(named) => {
let chosen = named
.named
.iter()
.find(|f| marked(&f.attrs))
.or_else(|| named.named.first());
match chosen {
Some(f) => {
let ident = f.ident.as_ref().expect("named field has ident");
Ok(quote!(#ident))
}
None => Err(syn::Error::new_spanned(
&named.named,
"MediaSink derive needs at least one field to delegate to",
)),
}
}
Fields::Unnamed(unnamed) => {
let idx = unnamed
.unnamed
.iter()
.position(|f| marked(&f.attrs))
.unwrap_or(0);
if unnamed.unnamed.is_empty() {
return Err(syn::Error::new_spanned(
&unnamed.unnamed,
"MediaSink derive needs at least one field to delegate to",
));
}
let index = Index::from(idx);
Ok(quote!(#index))
}
Fields::Unit => Err(syn::Error::new_spanned(
fields,
"MediaSink cannot be derived for a unit struct (nothing to delegate to)",
)),
}
}
#[proc_macro_attribute]
pub fn protocol(attr: TokenStream, item: TokenStream) -> TokenStream {
let lit = parse_macro_input!(attr as LitStr);
let input = parse_macro_input!(item as DeriveInput);
let name = &input.ident;
let (impl_g, ty_g, where_c) = input.generics.split_for_impl();
let value = lit.value();
quote! {
#input
impl #impl_g #name #ty_g #where_c {
pub const PROTOCOL_NAME: &'static str = #value;
}
}
.into()
}