commit b827fdd95041f110ce86ca6a0af25ee01ba0d460 Author: Lucio Franco Date: Fri Aug 9 14:01:27 2019 -0400 Inital commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..25c2376 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "tonic", + "tonic-macros" +] diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..040b985 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly-2019-08-09 diff --git a/tonic-macros/Cargo.toml b/tonic-macros/Cargo.toml new file mode 100644 index 0000000..faba5ab --- /dev/null +++ b/tonic-macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tonic-macros" +version = "0.1.0" +authors = ["Lucio Franco "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +tonic = { path = "../tonic" } +syn = { version = "0.15", features = ["full"] } +quote = "0.6" +proc-macro2 = "0.4" +prost-build = "0.5" +tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-future" } + +[dev-dependencies] +tokio = "=0.2.0-alpha.1" diff --git a/tonic-macros/src/lib.rs b/tonic-macros/src/lib.rs new file mode 100644 index 0000000..8101930 --- /dev/null +++ b/tonic-macros/src/lib.rs @@ -0,0 +1,98 @@ +#![feature(async_await)] + +extern crate proc_macro; +use proc_macro::TokenStream; +use prost_build::{Comments, Method, Service}; +use quote::quote; +use syn::{ImplItem, ImplItemMethod, ItemImpl, Type}; + +#[proc_macro_attribute] +pub fn grpc(attr: TokenStream, item: TokenStream) -> TokenStream { + let service = load_service(attr); + let mut original = item.clone(); + let ItemImpl { self_ty, items, .. } = syn::parse_macro_input!(item as ItemImpl); + + let s = if let Type::Path(t) = *self_ty { + t.path.segments.iter().next().unwrap().clone() + } else { + panic!("wrong type!") + }; + + let mut m_ident = None; + for item in items { + if let ImplItem::Method(method) = item { + // println!("{:?}", method); + + let ImplItemMethod { sig, .. } = method; + + if sig.asyncness.is_some() { + let name = format!("{}", sig.ident); + + if let Some(_method) = service.methods.iter().find(|method| method.name == name) { + // println!("found method!"); + m_ident = Some(sig.ident.clone()); + } + } + } + } + + // let ts = quote! { + // impl<'a> tower_service::Service> for #s { + // type Response = tonic::Response<()>; + // type Error = tonic::Status; + // type Future = tonic::ResponseFuture<'a, Self::Response, Self::Error>; + + // fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll> { + // std::task::Poll::Ready(Ok(())) + // } + + // fn call(&mut self, request: tonic::Request<()>) -> Self::Future { + // Box::pin(self.#m_ident(request)) + // } + // } + // }; + + let ts = quote! { + impl tonic::GrpcInnerService> for #s { + type Response = tonic::Response<()>; + + fn call<'a>(&'a mut self, request: tonic::Request<()>) -> tonic::ResponseFuture<'a, Self::Response> + where Self: 'a { + Box::pin(self.#m_ident(request)) + } + } + }; + + original.extend(TokenStream::from(ts)); + original +} + +fn load_service(_attr: TokenStream) -> Service { + Service { + name: "Greeter".into(), + proto_name: "greeter".into(), + package: "helloworld".into(), + comments: Comments { + leading_detached: Vec::new(), + leading: Vec::new(), + trailing: Vec::new(), + }, + methods: vec![Method { + name: "say_hello".into(), + proto_name: "SayHello".into(), + comments: Comments { + leading_detached: Vec::new(), + leading: Vec::new(), + trailing: Vec::new(), + }, + input_type: "HelloRequest".into(), + output_type: "HelloResponse".into(), + input_proto_type: "HelloRequest".into(), + output_proto_type: "HelloResponse".into(), + options: Default::default(), + client_streaming: false, + server_streaming: false, + }], + options: Default::default(), + } +} diff --git a/tonic-macros/tests/grpc.rs b/tonic-macros/tests/grpc.rs new file mode 100644 index 0000000..c4c61d9 --- /dev/null +++ b/tonic-macros/tests/grpc.rs @@ -0,0 +1,42 @@ +#![feature(async_await)] + +use tonic::{Request, Response, Status}; +use tonic_macros::grpc; +use tokio::timer::Delay; +use std::time::Duration; + +// #[derive(Debug)] +// struct HelloRequest; +// #[derive(Debug)] +// struct HelloResponse; + +#[derive(Default, Clone)] +struct MyGreeter { + data: String, +} + +#[grpc(service = "proto/helloworld.proto")] +impl MyGreeter { + pub async fn say_hello(&mut self, request: Request<()>) -> Result, Status> { + println!("Got a request: {:?}", request); + + let string = &self.data; + + let when = tokio::clock::now() + Duration::from_millis(100); + Delay::new(when).await; + + println!("My data: {:?}", string); + + Delay::new(when).await; + + Ok(Response::new(())) + } +} + +#[tokio::test] +async fn grpc() { + let mut greeter = MyGreeter { data: "some data".into()}; + + use tonic::GrpcInnerService; + greeter.call(Request::new(())).await.unwrap(); +} diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml new file mode 100644 index 0000000..74d9a0e --- /dev/null +++ b/tonic/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "tonic" +version = "0.1.0" +authors = ["Lucio Franco "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tower-grpc = { git = "https://github.com/tower-rs/tower-grpc", branch = "std-future" } diff --git a/tonic/src/lib.rs b/tonic/src/lib.rs new file mode 100644 index 0000000..1e5e4e4 --- /dev/null +++ b/tonic/src/lib.rs @@ -0,0 +1,14 @@ +pub use tower_grpc::*; + +use std::future::Future; +use std::pin::Pin; + +pub type ResponseFuture<'a, T> = Pin> + Send + 'a>>; + +pub trait GrpcInnerService { + type Response; + + fn call<'a>(&'a mut self, request: Request) -> ResponseFuture<'a, Self::Response> + where + Self: 'a; +}