chore: Fix feature flags and build docs (#311)

Signed-off-by: Lucio Franco <[email protected]>
This commit is contained in:
Lucio Franco
2020-03-30 12:02:10 -04:00
committed by GitHub
parent 59c7788846
commit 3cba9f5f6f
14 changed files with 258 additions and 247 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
[package]
name = "tonic-examples"
name = "examples"
version = "0.1.0"
authors = ["Lucio Franco <[email protected]>"]
edition = "2018"
@@ -107,7 +107,7 @@ name = "health-server"
path = "src/health/server.rs"
[dependencies]
tonic = { path = "../tonic", features = ["tls", "data-prost"] }
tonic = { path = "../tonic", features = ["tls"] }
prost = "0.6"
tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds"] }
futures = { version = "0.3", default-features = false, features = ["alloc"] }
+2 -2
View File
@@ -1,5 +1,5 @@
[package]
name = "tonic-interop"
name = "interop"
version = "0.1.0"
authors = ["Lucio Franco <[email protected]>"]
edition = "2018"
@@ -34,4 +34,4 @@ tracing-subscriber = "0.2.0-alpha"
tracing-log = "0.1.0"
[build-dependencies]
tonic-build = { path = "../tonic-build", features=["prost"] }
tonic-build = { path = "../tonic-build", features = ["prost"] }
+2 -2
View File
@@ -1,8 +1,8 @@
use interop::client;
use std::time::Duration;
use structopt::{clap::arg_enum, StructOpt};
use tonic::transport::Endpoint;
use tonic::transport::{Certificate, ClientTlsConfig};
use tonic_interop::client;
#[derive(StructOpt)]
struct Opts {
@@ -20,7 +20,7 @@ struct Opts {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_interop::trace_init();
interop::trace_init();
let matches = Opts::from_args();
+2 -2
View File
@@ -1,7 +1,7 @@
use interop::server;
use structopt::StructOpt;
use tonic::transport::Server;
use tonic::transport::{Identity, ServerTlsConfig};
use tonic_interop::server;
#[derive(StructOpt)]
struct Opts {
@@ -11,7 +11,7 @@ struct Opts {
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
tonic_interop::trace_init();
interop::trace_init();
let matches = Opts::from_args();
+5 -4
View File
@@ -1,9 +1,12 @@
use super::schema::{Method, Service};
use super::{Method, Service};
use crate::{generate_doc_comments, naive_snake_case};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
/// Generate service for client
/// Generate service for client.
///
/// This takes some `Service` and will generate a `TokenStream` that contains
/// a public module with the generated client.
pub fn generate<T: Service>(service: &T, proto_path: &str) -> TokenStream {
let service_ident = quote::format_ident!("{}Client", service.name());
let client_mod = quote::format_ident!("{}_client", naive_snake_case(&service.name()));
@@ -80,8 +83,6 @@ fn generate_methods<T: Service>(service: &T, proto_path: &str) -> TokenStream {
let mut stream = TokenStream::new();
for method in service.methods() {
use super::schema::Commentable;
let path = format!(
"/{}.{}/{}",
service.package(),
+61 -6
View File
@@ -55,23 +55,25 @@
#![doc(html_root_url = "https://docs.rs/tonic-build/0.1.0")]
#![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")]
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream};
use quote::TokenStreamExt;
/// Prost generator
#[cfg(feature = "prost")]
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
mod prost;
#[cfg(feature = "prost")]
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
pub use prost::{compile_protos, configure, Builder};
/// Traits to describe schema
pub mod schema;
#[cfg(feature = "rustfmt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustfmt")))]
use std::io::{self, Write};
#[cfg(feature = "rustfmt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustfmt")))]
use std::process::{exit, Command};
/// Service code generation for client
@@ -79,8 +81,63 @@ pub mod client;
/// Service code generation for Server
pub mod server;
/// Service generation trait.
///
/// This trait can be implemented and consumed
/// by `client::generate` and `server::generate`
/// to allow any codegen module to generate service
/// abstractions.
pub trait Service {
/// Path to the codec.
const CODEC_PATH: &'static str;
/// Comment type.
type Comment: AsRef<str>;
/// Method type.
type Method: Method;
/// Name of service.
fn name(&self) -> &str;
/// Package name of service.
fn package(&self) -> &str;
/// Identifier used to generate type name.
fn identifier(&self) -> &str;
/// Methods provided by service.
fn methods(&self) -> &[Self::Method];
/// Get comments about this item.
fn comment(&self) -> &[Self::Comment];
}
/// Method generation trait.
///
/// Each service contains a set of generic
/// `Methods`'s that will be used by codegen
/// to generate abstraction implementations for
/// the provided methods.
pub trait Method {
/// Path to the codec.
const CODEC_PATH: &'static str;
/// Comment type.
type Comment: AsRef<str>;
/// Name of method.
fn name(&self) -> &str;
/// Identifier used to generate type name.
fn identifier(&self) -> &str;
/// Method is streamed by client.
fn client_streaming(&self) -> bool;
/// Method is streamed by server.
fn server_streaming(&self) -> bool;
/// Get comments about this item.
fn comment(&self) -> &[Self::Comment];
/// Type name of request and response.
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream);
}
/// Format files under the out_dir with rustfmt
#[cfg(feature = "rustfmt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustfmt")))]
pub fn fmt(out_dir: &str) {
let dir = std::fs::read_dir(out_dir).unwrap();
@@ -129,9 +186,7 @@ fn generate_doc_comment<S: AsRef<str>>(comment: S) -> TokenStream {
}
// Generate a larger doc comment composed of many lines of doc comments
fn generate_doc_comments<'a, T: AsRef<str> + 'a, C: IntoIterator<Item = &'a T>>(
comments: C,
) -> TokenStream {
fn generate_doc_comments<T: AsRef<str>>(comments: &[T]) -> TokenStream {
let mut stream = TokenStream::new();
for comment in comments {
+13 -19
View File
@@ -1,4 +1,4 @@
use super::{client, schema, server};
use super::{client, server};
use proc_macro2::TokenStream;
use prost_build::{Config, Method, Service};
use quote::ToTokens;
@@ -7,18 +7,11 @@ use std::path::{Path, PathBuf};
const PROST_CODEC_PATH: &'static str = "tonic::codec::ProstCodec";
impl schema::Commentable for Service {
type Comment = String;
fn comment(&self) -> &[Self::Comment] {
&self.comments.leading[..]
}
}
impl schema::Service for Service {
impl crate::Service for Service {
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
type Method = Method;
type Comment = String;
fn name(&self) -> &str {
&self.name
@@ -32,21 +25,18 @@ impl schema::Service for Service {
&self.proto_name
}
fn comment(&self) -> &[Self::Comment] {
&self.comments.leading[..]
}
fn methods(&self) -> &[Self::Method] {
&self.methods[..]
}
}
impl schema::Commentable for Method {
type Comment = String;
fn comment(&self) -> &[Self::Comment] {
&self.comments.leading[..]
}
}
impl schema::Method for Method {
impl crate::Method for Method {
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
type Comment = String;
fn name(&self) -> &str {
&self.name
@@ -64,6 +54,10 @@ impl schema::Method for Method {
self.server_streaming
}
fn comment(&self) -> &[Self::Comment] {
&self.comments.leading[..]
}
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream) {
let request = if self.input_proto_type.starts_with(".google.protobuf")
|| self.input_type.starts_with("::")
-44
View File
@@ -1,44 +0,0 @@
use proc_macro2::TokenStream;
/// Item has comment
pub trait Commentable {
/// Comment type
type Comment: AsRef<str>;
/// Get comments about this item
fn comment(&self) -> &[Self::Comment];
}
/// Service
pub trait Service: Commentable {
/// Path to the codec
const CODEC_PATH: &'static str;
/// Method type
type Method: Method;
/// Name of service
fn name(&self) -> &str;
/// Package name of service
fn package(&self) -> &str;
/// Identifier used to generate type name
fn identifier(&self) -> &str;
/// Methods provided by service
fn methods(&self) -> &[Self::Method];
}
/// Method
pub trait Method: Commentable {
/// Path to the codec
const CODEC_PATH: &'static str;
/// Name of method
fn name(&self) -> &str;
/// Identifier used to generate type name
fn identifier(&self) -> &str;
/// Method is streamed by client
fn client_streaming(&self) -> bool;
/// Method is streamed by server
fn server_streaming(&self) -> bool;
/// Type name of request and response
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream);
}
+5 -2
View File
@@ -1,10 +1,13 @@
use super::schema::{Commentable, Method, Service};
use super::{Method, Service};
use crate::{generate_doc_comment, generate_doc_comments, naive_snake_case};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, Lit, LitStr};
/// Generate service for Server
/// Generate service for Server.
///
/// This takes some `Service` and will generate a `TokenStream` that contains
/// a public module containing the server service and handler trait.
pub fn generate<T: Service>(service: &T, proto_path: &str) -> TokenStream {
let methods = generate_methods(service, proto_path);
+2 -1
View File
@@ -14,12 +14,13 @@ categories = ["network-programming", "asynchronous"]
keywords = ["rpc", "grpc", "async", "healthcheck"]
[features]
default = ["transport"]
transport = ["tonic/transport"]
[dependencies]
async-stream = "0.2"
tokio = { version = "0.2", features = ["sync", "stream"] }
tonic = { version = "0.1", path = "../tonic", features = ["codegen", "data-prost"] }
tonic = { version = "0.1", path = "../tonic", features = ["codegen", "prost"] }
bytes = "0.5"
prost = "0.6"
+5 -5
View File
@@ -23,7 +23,7 @@ categories = ["web-programming", "network-programming", "asynchronous"]
keywords = ["rpc", "grpc", "async", "futures", "protobuf"]
[features]
default = ["transport", "codegen", "data-prost"]
default = ["transport", "codegen", "prost"]
codegen = ["async-trait"]
transport = [
"hyper",
@@ -35,7 +35,7 @@ transport = [
]
tls = ["transport", "tokio-rustls"]
tls-roots = ["tls", "rustls-native-certs"]
data-prost = ["prost", "prost-derive"]
prost = ["prost1", "prost-derive"]
# [[bench]]
# name = "bench_main"
@@ -57,15 +57,15 @@ http-body = "0.3"
pin-project = "0.4"
# prost
prost = { version = "0.6", optional = true }
prost1 = { package = "prost", version = "0.6", optional = true }
prost-derive = { version = "0.6", optional = true }
# codegen
async-trait = { version = "0.1.13", optional = true }
# transport
hyper = { version = "0.13", features = ["stream"], optional = true }
tokio = { version = "0.2", features = ["tcp"], optional = true }
hyper = { version = "0.13.4", features = ["stream"], optional = true }
tokio = { version = "0.2.13", features = ["tcp"], optional = true }
tower = { version = "0.3", optional = true}
tower-make = { version = "0.3", features = ["connect"] }
tower-balance = { version = "0.3", optional = true }
+3 -6
View File
@@ -6,18 +6,15 @@
mod buffer;
mod decode;
mod encode;
#[cfg(feature = "data-prost")]
#[cfg(feature = "prost")]
mod prost;
#[cfg(all(test, feature = "data-prost"))]
mod prost_tests;
use std::io;
pub use self::decode::Streaming;
pub(crate) use self::encode::{encode_client, encode_server};
#[cfg(feature = "data-prost")]
#[cfg_attr(docsrs, doc(cfg(feature = "data-prost")))]
#[cfg(feature = "prost")]
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
pub use self::prost::ProstCodec;
use crate::Status;
pub use buffer::{DecodeBuf, EncodeBuf};
+156 -2
View File
@@ -1,7 +1,7 @@
use super::{Codec, DecodeBuf, Decoder, Encoder};
use crate::codec::EncodeBuf;
use crate::{Code, Status};
use prost::Message;
use prost1::Message;
use std::marker::PhantomData;
/// A [`Codec`] that implements `application/grpc+proto` via the prost library..
@@ -69,8 +69,162 @@ impl<U: Message + Default> Decoder for ProstDecoder<U> {
}
}
fn from_decode_error(error: prost::DecodeError) -> crate::Status {
fn from_decode_error(error: prost1::DecodeError) -> crate::Status {
// Map Protobuf parse errors to an INTERNAL status code, as per
// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
Status::new(Code::Internal, error.to_string())
}
#[cfg(tests)]
mod tests {
use super::{encode_server, Decoder, Encoder, Streaming};
use crate::codec::buffer::DecodeBuf;
use crate::codec::EncodeBuf;
use crate::Status;
use bytes::{Buf, BufMut, BytesMut};
use http_body::Body;
const LEN: usize = 10000;
#[tokio::test]
async fn decode() {
let decoder = MockDecoder::default();
let msg = vec![0u8; LEN];
let mut buf = BytesMut::new();
buf.reserve(msg.len() + 5);
buf.put_u8(0);
buf.put_u32(msg.len() as u32);
buf.put(&msg[..]);
let body = body::MockBody::new(&buf[..], 10005, 0);
let mut stream = Streaming::new_request(decoder, body);
let mut i = 0usize;
while let Some(output_msg) = stream.message().await.unwrap() {
assert_eq!(output_msg.len(), msg.len());
i += 1;
}
assert_eq!(i, 1);
}
#[tokio::test]
async fn encode() {
let encoder = MockEncoder::default();
let msg = Vec::from(&[0u8; 1024][..]);
let messages = std::iter::repeat(Ok::<_, Status>(msg)).take(10000);
let source = futures_util::stream::iter(messages);
let body = encode_server(encoder, source);
futures_util::pin_mut!(body);
while let Some(r) = body.data().await {
r.unwrap();
}
}
#[derive(Debug, Clone, Default)]
struct MockEncoder;
impl Encoder for MockEncoder {
type Item = Vec<u8>;
type Error = Status;
fn encode(&mut self, item: Self::Item, buf: &mut EncodeBuf<'_>) -> Result<(), Self::Error> {
buf.put(&item[..]);
Ok(())
}
}
#[derive(Debug, Clone, Default)]
struct MockDecoder;
impl Decoder for MockDecoder {
type Item = Vec<u8>;
type Error = Status;
fn decode(&mut self, buf: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
let out = Vec::from(buf.bytes());
buf.advance(LEN);
Ok(Some(out))
}
}
mod body {
use crate::Status;
use bytes::Bytes;
use http_body::Body;
use std::{
pin::Pin,
task::{Context, Poll},
};
#[derive(Debug)]
pub(super) struct MockBody {
data: Bytes,
// the size of the partial message to send
partial_len: usize,
// the number of times we've sent
count: usize,
}
impl MockBody {
pub(super) fn new(b: &[u8], partial_len: usize, count: usize) -> Self {
MockBody {
data: Bytes::copy_from_slice(&b[..]),
partial_len,
count,
}
}
}
impl Body for MockBody {
type Data = Bytes;
type Error = Status;
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
// every other call to poll_data returns data
let should_send = self.count % 2 == 0;
let data_len = self.data.len();
let partial_len = self.partial_len;
let count = self.count;
if data_len > 0 {
let result = if should_send {
let response =
self.data
.split_to(if count == 0 { partial_len } else { data_len });
Poll::Ready(Some(Ok(response)))
} else {
cx.waker().wake_by_ref();
Poll::Pending
};
// make some fake progress
self.count += 1;
result
} else {
Poll::Ready(None)
}
}
fn poll_trailers(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
drop(cx);
Poll::Ready(Ok(None))
}
}
}
}
-150
View File
@@ -1,150 +0,0 @@
use super::{encode_server, Decoder, Encoder, Streaming};
use crate::codec::buffer::DecodeBuf;
use crate::codec::EncodeBuf;
use crate::Status;
use bytes::{Buf, BufMut, BytesMut};
use http_body::Body;
const LEN: usize = 10000;
#[tokio::test]
async fn decode() {
let decoder = MockDecoder::default();
let msg = vec![0u8; LEN];
let mut buf = BytesMut::new();
buf.reserve(msg.len() + 5);
buf.put_u8(0);
buf.put_u32(msg.len() as u32);
buf.put(&msg[..]);
let body = body::MockBody::new(&buf[..], 10005, 0);
let mut stream = Streaming::new_request(decoder, body);
let mut i = 0usize;
while let Some(output_msg) = stream.message().await.unwrap() {
assert_eq!(output_msg.len(), msg.len());
i += 1;
}
assert_eq!(i, 1);
}
#[tokio::test]
async fn encode() {
let encoder = MockEncoder::default();
let msg = Vec::from(&[0u8; 1024][..]);
let messages = std::iter::repeat(Ok::<_, Status>(msg)).take(10000);
let source = futures_util::stream::iter(messages);
let body = encode_server(encoder, source);
futures_util::pin_mut!(body);
while let Some(r) = body.data().await {
r.unwrap();
}
}
#[derive(Debug, Clone, Default)]
struct MockEncoder;
impl Encoder for MockEncoder {
type Item = Vec<u8>;
type Error = Status;
fn encode(&mut self, item: Self::Item, buf: &mut EncodeBuf<'_>) -> Result<(), Self::Error> {
buf.put(&item[..]);
Ok(())
}
}
#[derive(Debug, Clone, Default)]
struct MockDecoder;
impl Decoder for MockDecoder {
type Item = Vec<u8>;
type Error = Status;
fn decode(&mut self, buf: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
let out = Vec::from(buf.bytes());
buf.advance(LEN);
Ok(Some(out))
}
}
mod body {
use crate::Status;
use bytes::Bytes;
use http_body::Body;
use std::{
pin::Pin,
task::{Context, Poll},
};
#[derive(Debug)]
pub(super) struct MockBody {
data: Bytes,
// the size of the partial message to send
partial_len: usize,
// the number of times we've sent
count: usize,
}
impl MockBody {
pub(super) fn new(b: &[u8], partial_len: usize, count: usize) -> Self {
MockBody {
data: Bytes::copy_from_slice(&b[..]),
partial_len,
count,
}
}
}
impl Body for MockBody {
type Data = Bytes;
type Error = Status;
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
// every other call to poll_data returns data
let should_send = self.count % 2 == 0;
let data_len = self.data.len();
let partial_len = self.partial_len;
let count = self.count;
if data_len > 0 {
let result = if should_send {
let response =
self.data
.split_to(if count == 0 { partial_len } else { data_len });
Poll::Ready(Some(Ok(response)))
} else {
cx.waker().wake_by_ref();
Poll::Pending
};
// make some fake progress
self.count += 1;
result
} else {
Poll::Ready(None)
}
}
fn poll_trailers(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
drop(cx);
Poll::Ready(Ok(None))
}
}
}