Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit caf9eb3e authored by Zach Johnson's avatar Zach Johnson Committed by Automerger Merge Worker
Browse files

rusty-gd: remove unused grpc-macros content am: 39c5f556 am: 5d6b30de am: 66d117d6

Original change: https://android-review.googlesource.com/c/platform/system/bt/+/1558531

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I35f54099c135ef4ab7c56566cbeecc1d3773fde9
parents 151a1741 66d117d6
Loading
Loading
Loading
Loading
+0 −272
Original line number Diff line number Diff line
//! Macros simplifying grpc service definitions

extern crate proc_macro;
use proc_macro::{TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned};
use syn::parse::{Parse, ParseStream, Result};
use syn::{
    braced, parenthesized, parse_macro_input, parse_quote, Block, Expr, FnArg, Ident,
    ImplItemMethod, PatType, Path, Receiver, Token, Type,
};

/// provices shortcut syntax for defining proto-based rpc services
#[proc_macro]
pub fn grpc_service(item: TokenStream) -> TokenStream {
    let service = parse_macro_input!(item as ServiceDef);
    let grpc_trait = service.grpc_trait.clone();
    let struct_ = service.struct_.clone();
    let struct_for_facade = service.struct_.clone();
    let functions = service.items.iter().map(|i| match i {
        ServiceItem::Raw(inner) => Some(inner.clone()),
        ServiceItem::Rpc(inner) => Some(inner.clone().generate_fn()),
    });

    let mut grpc_path = grpc_trait.clone().segments;
    let grpc_create_fn = format_ident!(
        "create_{}",
        to_snake_case(&grpc_path.pop().unwrap().into_value().ident.to_string().as_str())
    );

    let emitted_code = quote! {
        impl #grpc_trait for #struct_ {
            #(#functions)*
        }

        impl bt_common::GrpcFacade for #struct_for_facade {
            fn into_grpc(self) -> grpcio::Service {
                #grpc_path#grpc_create_fn(self)
            }
        }
    };

    emitted_code.into()
}

struct ServiceDef {
    grpc_trait: Path,
    struct_: Type,
    items: Vec<ServiceItem>,
}

enum ServiceItem {
    Raw(ImplItemMethod),
    Rpc(RpcItem),
}

#[derive(Clone)]
struct RpcItem {
    name: Ident,
    input: PatType,
    output: RpcReturnType,
    unimplemented: bool,
    drain: Option<Box<Expr>>,
    code: Option<Block>,
}

impl RpcItem {
    fn generate_fn(self) -> ImplItemMethod {
        let name = self.name;
        let input = self.input;
        let output = self.output.type_;
        let tokens = match (self.drain, self.code) {
            (Some(drain), None) if self.output.stream => {
                quote_spanned! {
                    name.span()=>
                    fn #name(&mut self, _ctx: grpcio::RpcContext<'_>, #input, mut sink: grpcio::ServerStreamingSink<#output>) {
                        let stream = #drain.clone();
                        self.rt.spawn(async move {
                            while let Some(item) = stream.lock().await.recv().await {
                                sink.send((item.to_proto(), grpcio::WriteFlags::default())).await.unwrap();
                            }
                        });
                    }
                }
            }
            (Some(drain), None) if !self.output.stream => {
                let input_pat = input.clone().pat;
                quote_spanned! {
                    name.span()=>
                    fn #name(&mut self, _ctx: grpcio::RpcContext<'_>, #input, sink: grpcio::UnarySink<#output>) {
                        let channel = #drain.clone();
                        self.rt.block_on(async move {
                            channel.send(#input_pat.to_packet()).await.unwrap();
                        });
                        sink.success(Empty::default());
                    }
                }
            }
            (None, Some(code)) if !self.output.stream => {
                let tokens = quote! { #code };
                let rewritten_code = syn::parse::<Block>(replace_self(tokens.into())).unwrap();
                quote_spanned! {
                    name.span()=>
                    fn #name(&mut self, _ctx: grpcio::RpcContext<'_>, #input, sink: grpcio::UnarySink<#output>) {
                        let mut ___implicit_self___ = self.clone();
                        self.rt.block_on(async move {
                            #rewritten_code
                        });
                        sink.success(Empty::default());
                    }
                }
            }
            (None, None) if self.unimplemented => {
                let sink_type = format_ident!(
                    "{}",
                    if self.output.stream { "ServerStreamingSink" } else { "UnarySink" }
                );
                quote_spanned! {
                    name.span()=>
                    fn #name(&mut self, _ctx: grpcio::RpcContext<'_>, #input, _sink: grpcio::#sink_type<#output>) {
                        unimplemented!();
                    }
                }
            }
            (_, _) => {
                let sink_type = format_ident!(
                    "{}",
                    if self.output.stream { "ServerStreamingSink" } else { "UnarySink" }
                );
                quote_spanned! {
                    name.span()=>
                    fn #name(&mut self, _ctx: grpcio::RpcContext<'_>, #input, _sink: grpcio::#sink_type<#output>) {
                        compile_error!("support for this syntax is not supported yet");
                    }
                }
            }
        };

        syn::parse(tokens.into()).unwrap()
    }
}

#[derive(Clone)]
struct RpcReturnType {
    type_: Type,
    stream: bool,
}

impl Parse for ServiceDef {
    fn parse(input: ParseStream) -> Result<Self> {
        let _impl_token: Token![impl] = input.parse()?;
        let grpc_trait = input.parse()?;
        let _for: Token![for] = input.parse()?;
        let struct_ = input.parse()?;
        let content;
        braced!(content in input);

        let mut items = Vec::new();
        while !content.is_empty() {
            items.push(content.parse()?);
        }

        Ok(ServiceDef { grpc_trait, struct_, items })
    }
}

impl Parse for ServiceItem {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.peek(Token![fn]) {
            Ok(ServiceItem::Raw(input.parse()?))
        } else {
            match input.parse::<Ident>()?.to_string().as_str() {
                "rpc" => Ok(ServiceItem::Rpc(input.parse()?)),
                keyword => panic!("unexpected keyword {}", keyword),
            }
        }
    }
}

impl Parse for RpcItem {
    fn parse(input: ParseStream) -> Result<Self> {
        let name: Ident = input.parse()?;
        let rpc_input;
        parenthesized!(rpc_input in input);
        let receiver: Receiver = rpc_input.parse()?;
        if receiver.mutability.is_none() {
            panic!("self should be mutable");
        }
        if receiver.reference.is_none() {
            panic!("self should be by reference");
        }
        let rpc_input: FnArg = if rpc_input.is_empty() {
            parse_quote! {
                _arg: Empty
            }
        } else {
            rpc_input.parse::<Token![,]>()?;
            rpc_input.parse()?
        };
        let rpc_input = match rpc_input {
            FnArg::Receiver(r) => panic!("did not expect {:?}", r),
            FnArg::Typed(t) => t,
        };

        let output = if input.peek(Token![->]) {
            let _arrow: Token![->] = input.parse()?;
            let stream = input.peek2(Ident);
            if stream && input.parse::<Ident>()?.to_string().as_str() != "stream" {
                panic!("expected \'stream\' keyword");
            }
            RpcReturnType { type_: input.parse()?, stream }
        } else {
            RpcReturnType {
                type_: parse_quote! {
                    Empty
                },
                stream: false,
            }
        };
        let (unimplemented, drain, code) = if input.peek(Token![=>]) {
            input.parse::<Token![=>]>()?;
            match input.parse::<Ident>()?.to_string().as_str() {
                "unimplemented" => {
                    input.parse::<Token![!]>()?;
                    let contents;
                    parenthesized!(contents in input);
                    if !contents.is_empty() {
                        panic!("expected empty unimplemented!()");
                    }
                    (true, None, None)
                }
                "drains" if output.stream => (false, Some(input.parse()?), None),
                "into" if !output.stream => (false, Some(input.parse()?), None),
                keyword => panic!("unexpected keyword {}", keyword),
            }
        } else {
            (false, None, Some(input.parse()?))
        };

        Ok(RpcItem { name, input: rpc_input, output, unimplemented, drain, code })
    }
}

fn to_snake_case(s: &str) -> String {
    let mut output = String::default();
    let mut first = true;
    for c in s.chars() {
        if c.is_uppercase() && !first {
            output.push('_');
        }
        output.push_str(&c.to_lowercase().to_string());
        first = false;
    }

    output
}

fn replace_self(stream: TokenStream) -> TokenStream {
    stream
        .into_iter()
        .map(|tt| match tt {
            TokenTree::Ident(i) if i.to_string() == "self" => {
                TokenTree::Ident(proc_macro::Ident::new("___implicit_self___", i.span()))
            }
            TokenTree::Group(g) => {
                let mut group = proc_macro::Group::new(g.delimiter(), replace_self(g.stream()));
                group.set_span(g.span());
                TokenTree::Group(group)
            }
            other => other,
        })
        .collect()
}