drone-core-macros 0.14.3

Procedural macros for drone-core.
Documentation
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
    braced,
    parse::{Parse, ParseStream, Result},
    parse_macro_input, Attribute, ExprPath, Ident, Token, Visibility,
};

struct Input {
    thr: Thr,
    local: Local,
    index: Index,
    threads: Threads,
    resume: Option<ExprPath>,
    set_pending: Option<ExprPath>,
}

struct Thr {
    attrs: Vec<Attribute>,
    vis: Visibility,
    ident: Ident,
    tokens: TokenStream2,
}

struct Local {
    attrs: Vec<Attribute>,
    vis: Visibility,
    ident: Ident,
    tokens: TokenStream2,
}

struct Index {
    attrs: Vec<Attribute>,
    vis: Visibility,
    ident: Ident,
}

struct Threads {
    tokens: TokenStream2,
}

impl Parse for Input {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let mut thr = None;
        let mut local = None;
        let mut index = None;
        let mut threads = None;
        let mut resume = None;
        let mut set_pending = None;
        while !input.is_empty() {
            let attrs = input.call(Attribute::parse_outer)?;
            let ident = input.parse::<Ident>()?;
            input.parse::<Token![=>]>()?;
            if ident == "thread" {
                if thr.is_none() {
                    thr = Some(Thr::parse(input, attrs)?);
                } else {
                    return Err(input.error("multiple `thread` specifications"));
                }
            } else if ident == "local" {
                if local.is_none() {
                    local = Some(Local::parse(input, attrs)?);
                } else {
                    return Err(input.error("multiple `local` specifications"));
                }
            } else if ident == "index" {
                if index.is_none() {
                    index = Some(Index::parse(input, attrs)?);
                } else {
                    return Err(input.error("multiple `index` specifications"));
                }
            } else if attrs.is_empty() && ident == "threads" {
                if threads.is_none() {
                    threads = Some(input.parse()?);
                } else {
                    return Err(input.error("multiple `threads` specifications"));
                }
            } else if attrs.is_empty() && ident == "resume" {
                if resume.is_none() {
                    resume = Some(input.parse()?);
                } else {
                    return Err(input.error("multiple `resume` specifications"));
                }
            } else if attrs.is_empty() && ident == "set_pending" {
                if set_pending.is_none() {
                    set_pending = Some(input.parse()?);
                } else {
                    return Err(input.error("multiple `set_pending` specifications"));
                }
            } else {
                return Err(input.error(format!("unknown key: `{}`", ident)));
            }
            if !input.is_empty() {
                input.parse::<Token![;]>()?;
            }
        }
        Ok(Self {
            thr: thr.ok_or_else(|| input.error("missing `thread` specification"))?,
            local: local.ok_or_else(|| input.error("missing `local` specification"))?,
            index: index.ok_or_else(|| input.error("missing `index` specification"))?,
            threads: threads.ok_or_else(|| input.error("missing `threads` specification"))?,
            resume,
            set_pending,
        })
    }
}

impl Thr {
    fn parse(input: ParseStream<'_>, attrs: Vec<Attribute>) -> Result<Self> {
        let vis = input.parse()?;
        let ident = input.parse()?;
        let input2;
        braced!(input2 in input);
        let tokens = input2.parse()?;
        Ok(Self { attrs, vis, ident, tokens })
    }
}

impl Local {
    fn parse(input: ParseStream<'_>, attrs: Vec<Attribute>) -> Result<Self> {
        let vis = input.parse()?;
        let ident = input.parse()?;
        let input2;
        braced!(input2 in input);
        let tokens = input2.parse()?;
        Ok(Self { attrs, vis, ident, tokens })
    }
}

impl Index {
    fn parse(input: ParseStream<'_>, attrs: Vec<Attribute>) -> Result<Self> {
        let vis = input.parse()?;
        let ident = input.parse()?;
        Ok(Self { attrs, vis, ident })
    }
}

impl Parse for Threads {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let input2;
        braced!(input2 in input);
        let tokens = input2.parse()?;
        Ok(Self { tokens })
    }
}

pub fn proc_macro(input: TokenStream) -> TokenStream {
    let Input { thr, local, index, threads, resume, set_pending } = parse_macro_input!(input);
    let def_pool = def_pool(&thr, &local, &index, &threads, resume.as_ref());
    let def_soft = def_soft(&thr, set_pending.as_ref());

    let expanded = quote! {
        #def_pool
        #def_soft
    };
    expanded.into()
}

fn def_pool(
    thr: &Thr,
    local: &Local,
    index: &Index,
    threads: &Threads,
    resume: Option<&ExprPath>,
) -> TokenStream2 {
    let Thr { attrs: thr_attrs, vis: thr_vis, ident: thr_ident, tokens: thr_tokens } = thr;
    let Local { attrs: local_attrs, vis: local_vis, ident: local_ident, tokens: local_tokens } =
        local;
    let Index { attrs: index_attrs, vis: index_vis, ident: index_ident } = index;
    let Threads { tokens: threads_tokens } = threads;
    let resume = resume.into_iter();

    quote! {
        ::drone_core::thr::pool! {
            #(#thr_attrs)*
            thread => #thr_vis #thr_ident {
                priority: ::core::sync::atomic::AtomicU8 = ::core::sync::atomic::AtomicU8::new(0);
                #thr_tokens
            };

            #(#local_attrs)*
            local => #local_vis #local_ident {
                #local_tokens
            };

            #(#index_attrs)*
            index => #index_vis #index_ident;

            threads => {
                #threads_tokens
            };

            #(resume => #resume;)*
        }
    }
}

fn def_soft(thr: &Thr, set_pending: Option<&ExprPath>) -> TokenStream2 {
    let Thr { ident: thr_ident, .. } = thr;
    let set_pending = set_pending.map(|set_pending| {
        quote! {
            #[inline]
            unsafe fn set_pending(thr_idx: u16) {
                unsafe { #set_pending(thr_idx) };
            }
        }
    });

    quote! {
        unsafe impl ::drone_core::thr::SoftThread for #thr_ident {
            #[inline]
            fn pending() -> *const ::core::sync::atomic::AtomicU32 {
                #[allow(clippy::declare_interior_mutable_const)]
                const VALUE: ::core::sync::atomic::AtomicU32 =
                    ::core::sync::atomic::AtomicU32::new(0);
                const COUNT: usize = ::drone_core::thr::pending_size::<#thr_ident>();
                static PENDING: [::core::sync::atomic::AtomicU32; COUNT] = [VALUE; COUNT];
                PENDING.as_ptr()
            }

            #[inline]
            fn pending_priority() -> *const ::core::sync::atomic::AtomicU8 {
                static PENDING_PRIORITY: ::core::sync::atomic::AtomicU8 =
                    ::core::sync::atomic::AtomicU8::new(0);
                &PENDING_PRIORITY
            }

            #[inline]
            fn priority(&self) -> *const ::core::sync::atomic::AtomicU8 {
                &self.priority
            }

            #set_pending
        }
    }
}