chore: Fix feature flags and build docs (#311)
Signed-off-by: Lucio Franco <[email protected]>
This commit is contained in:
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tonic-examples"
|
name = "examples"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Lucio Franco <[email protected]>"]
|
authors = ["Lucio Franco <[email protected]>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -107,7 +107,7 @@ name = "health-server"
|
|||||||
path = "src/health/server.rs"
|
path = "src/health/server.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tonic = { path = "../tonic", features = ["tls", "data-prost"] }
|
tonic = { path = "../tonic", features = ["tls"] }
|
||||||
prost = "0.6"
|
prost = "0.6"
|
||||||
tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds"] }
|
tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds"] }
|
||||||
futures = { version = "0.3", default-features = false, features = ["alloc"] }
|
futures = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tonic-interop"
|
name = "interop"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Lucio Franco <[email protected]>"]
|
authors = ["Lucio Franco <[email protected]>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -34,4 +34,4 @@ tracing-subscriber = "0.2.0-alpha"
|
|||||||
tracing-log = "0.1.0"
|
tracing-log = "0.1.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = { path = "../tonic-build", features=["prost"] }
|
tonic-build = { path = "../tonic-build", features = ["prost"] }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use interop::client;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use structopt::{clap::arg_enum, StructOpt};
|
use structopt::{clap::arg_enum, StructOpt};
|
||||||
use tonic::transport::Endpoint;
|
use tonic::transport::Endpoint;
|
||||||
use tonic::transport::{Certificate, ClientTlsConfig};
|
use tonic::transport::{Certificate, ClientTlsConfig};
|
||||||
use tonic_interop::client;
|
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct Opts {
|
struct Opts {
|
||||||
@@ -20,7 +20,7 @@ struct Opts {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tonic_interop::trace_init();
|
interop::trace_init();
|
||||||
|
|
||||||
let matches = Opts::from_args();
|
let matches = Opts::from_args();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use interop::server;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
use tonic::transport::{Identity, ServerTlsConfig};
|
use tonic::transport::{Identity, ServerTlsConfig};
|
||||||
use tonic_interop::server;
|
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct Opts {
|
struct Opts {
|
||||||
@@ -11,7 +11,7 @@ struct Opts {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||||
tonic_interop::trace_init();
|
interop::trace_init();
|
||||||
|
|
||||||
let matches = Opts::from_args();
|
let matches = Opts::from_args();
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use super::schema::{Method, Service};
|
use super::{Method, Service};
|
||||||
use crate::{generate_doc_comments, naive_snake_case};
|
use crate::{generate_doc_comments, naive_snake_case};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
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 {
|
pub fn generate<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let service_ident = quote::format_ident!("{}Client", service.name());
|
let service_ident = quote::format_ident!("{}Client", service.name());
|
||||||
let client_mod = quote::format_ident!("{}_client", naive_snake_case(&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();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
for method in service.methods() {
|
for method in service.methods() {
|
||||||
use super::schema::Commentable;
|
|
||||||
|
|
||||||
let path = format!(
|
let path = format!(
|
||||||
"/{}.{}/{}",
|
"/{}.{}/{}",
|
||||||
service.package(),
|
service.package(),
|
||||||
|
|||||||
+61
-6
@@ -55,23 +55,25 @@
|
|||||||
#![doc(html_root_url = "https://docs.rs/tonic-build/0.1.0")]
|
#![doc(html_root_url = "https://docs.rs/tonic-build/0.1.0")]
|
||||||
#![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")]
|
#![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")]
|
||||||
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
#![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 proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream};
|
||||||
use quote::TokenStreamExt;
|
use quote::TokenStreamExt;
|
||||||
|
|
||||||
/// Prost generator
|
/// Prost generator
|
||||||
#[cfg(feature = "prost")]
|
#[cfg(feature = "prost")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
|
||||||
mod prost;
|
mod prost;
|
||||||
|
|
||||||
#[cfg(feature = "prost")]
|
#[cfg(feature = "prost")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
|
||||||
pub use prost::{compile_protos, configure, Builder};
|
pub use prost::{compile_protos, configure, Builder};
|
||||||
|
|
||||||
/// Traits to describe schema
|
|
||||||
pub mod schema;
|
|
||||||
|
|
||||||
#[cfg(feature = "rustfmt")]
|
#[cfg(feature = "rustfmt")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "rustfmt")))]
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
#[cfg(feature = "rustfmt")]
|
#[cfg(feature = "rustfmt")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "rustfmt")))]
|
||||||
use std::process::{exit, Command};
|
use std::process::{exit, Command};
|
||||||
|
|
||||||
/// Service code generation for client
|
/// Service code generation for client
|
||||||
@@ -79,8 +81,63 @@ pub mod client;
|
|||||||
/// Service code generation for Server
|
/// Service code generation for Server
|
||||||
pub mod 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
|
/// Format files under the out_dir with rustfmt
|
||||||
#[cfg(feature = "rustfmt")]
|
#[cfg(feature = "rustfmt")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "rustfmt")))]
|
||||||
pub fn fmt(out_dir: &str) {
|
pub fn fmt(out_dir: &str) {
|
||||||
let dir = std::fs::read_dir(out_dir).unwrap();
|
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
|
// 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>>(
|
fn generate_doc_comments<T: AsRef<str>>(comments: &[T]) -> TokenStream {
|
||||||
comments: C,
|
|
||||||
) -> TokenStream {
|
|
||||||
let mut stream = TokenStream::new();
|
let mut stream = TokenStream::new();
|
||||||
|
|
||||||
for comment in comments {
|
for comment in comments {
|
||||||
|
|||||||
+13
-19
@@ -1,4 +1,4 @@
|
|||||||
use super::{client, schema, server};
|
use super::{client, server};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use prost_build::{Config, Method, Service};
|
use prost_build::{Config, Method, Service};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
@@ -7,18 +7,11 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
const PROST_CODEC_PATH: &'static str = "tonic::codec::ProstCodec";
|
const PROST_CODEC_PATH: &'static str = "tonic::codec::ProstCodec";
|
||||||
|
|
||||||
impl schema::Commentable for Service {
|
impl crate::Service for Service {
|
||||||
type Comment = String;
|
|
||||||
|
|
||||||
fn comment(&self) -> &[Self::Comment] {
|
|
||||||
&self.comments.leading[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl schema::Service for Service {
|
|
||||||
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
|
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
|
||||||
|
|
||||||
type Method = Method;
|
type Method = Method;
|
||||||
|
type Comment = String;
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -32,21 +25,18 @@ impl schema::Service for Service {
|
|||||||
&self.proto_name
|
&self.proto_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn comment(&self) -> &[Self::Comment] {
|
||||||
|
&self.comments.leading[..]
|
||||||
|
}
|
||||||
|
|
||||||
fn methods(&self) -> &[Self::Method] {
|
fn methods(&self) -> &[Self::Method] {
|
||||||
&self.methods[..]
|
&self.methods[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl schema::Commentable for Method {
|
impl crate::Method for Method {
|
||||||
type Comment = String;
|
|
||||||
|
|
||||||
fn comment(&self) -> &[Self::Comment] {
|
|
||||||
&self.comments.leading[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl schema::Method for Method {
|
|
||||||
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
|
const CODEC_PATH: &'static str = PROST_CODEC_PATH;
|
||||||
|
type Comment = String;
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
@@ -64,6 +54,10 @@ impl schema::Method for Method {
|
|||||||
self.server_streaming
|
self.server_streaming
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn comment(&self) -> &[Self::Comment] {
|
||||||
|
&self.comments.leading[..]
|
||||||
|
}
|
||||||
|
|
||||||
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream) {
|
fn request_response_name(&self, proto_path: &str) -> (TokenStream, TokenStream) {
|
||||||
let request = if self.input_proto_type.starts_with(".google.protobuf")
|
let request = if self.input_proto_type.starts_with(".google.protobuf")
|
||||||
|| self.input_type.starts_with("::")
|
|| self.input_type.starts_with("::")
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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 crate::{generate_doc_comment, generate_doc_comments, naive_snake_case};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Ident, Lit, LitStr};
|
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 {
|
pub fn generate<T: Service>(service: &T, proto_path: &str) -> TokenStream {
|
||||||
let methods = generate_methods(service, proto_path);
|
let methods = generate_methods(service, proto_path);
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ categories = ["network-programming", "asynchronous"]
|
|||||||
keywords = ["rpc", "grpc", "async", "healthcheck"]
|
keywords = ["rpc", "grpc", "async", "healthcheck"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["transport"]
|
||||||
transport = ["tonic/transport"]
|
transport = ["tonic/transport"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-stream = "0.2"
|
async-stream = "0.2"
|
||||||
tokio = { version = "0.2", features = ["sync", "stream"] }
|
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"
|
bytes = "0.5"
|
||||||
prost = "0.6"
|
prost = "0.6"
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -23,7 +23,7 @@ categories = ["web-programming", "network-programming", "asynchronous"]
|
|||||||
keywords = ["rpc", "grpc", "async", "futures", "protobuf"]
|
keywords = ["rpc", "grpc", "async", "futures", "protobuf"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["transport", "codegen", "data-prost"]
|
default = ["transport", "codegen", "prost"]
|
||||||
codegen = ["async-trait"]
|
codegen = ["async-trait"]
|
||||||
transport = [
|
transport = [
|
||||||
"hyper",
|
"hyper",
|
||||||
@@ -35,7 +35,7 @@ transport = [
|
|||||||
]
|
]
|
||||||
tls = ["transport", "tokio-rustls"]
|
tls = ["transport", "tokio-rustls"]
|
||||||
tls-roots = ["tls", "rustls-native-certs"]
|
tls-roots = ["tls", "rustls-native-certs"]
|
||||||
data-prost = ["prost", "prost-derive"]
|
prost = ["prost1", "prost-derive"]
|
||||||
|
|
||||||
# [[bench]]
|
# [[bench]]
|
||||||
# name = "bench_main"
|
# name = "bench_main"
|
||||||
@@ -57,15 +57,15 @@ http-body = "0.3"
|
|||||||
pin-project = "0.4"
|
pin-project = "0.4"
|
||||||
|
|
||||||
# prost
|
# prost
|
||||||
prost = { version = "0.6", optional = true }
|
prost1 = { package = "prost", version = "0.6", optional = true }
|
||||||
prost-derive = { version = "0.6", optional = true }
|
prost-derive = { version = "0.6", optional = true }
|
||||||
|
|
||||||
# codegen
|
# codegen
|
||||||
async-trait = { version = "0.1.13", optional = true }
|
async-trait = { version = "0.1.13", optional = true }
|
||||||
|
|
||||||
# transport
|
# transport
|
||||||
hyper = { version = "0.13", features = ["stream"], optional = true }
|
hyper = { version = "0.13.4", features = ["stream"], optional = true }
|
||||||
tokio = { version = "0.2", features = ["tcp"], optional = true }
|
tokio = { version = "0.2.13", features = ["tcp"], optional = true }
|
||||||
tower = { version = "0.3", optional = true}
|
tower = { version = "0.3", optional = true}
|
||||||
tower-make = { version = "0.3", features = ["connect"] }
|
tower-make = { version = "0.3", features = ["connect"] }
|
||||||
tower-balance = { version = "0.3", optional = true }
|
tower-balance = { version = "0.3", optional = true }
|
||||||
|
|||||||
@@ -6,18 +6,15 @@
|
|||||||
mod buffer;
|
mod buffer;
|
||||||
mod decode;
|
mod decode;
|
||||||
mod encode;
|
mod encode;
|
||||||
#[cfg(feature = "data-prost")]
|
#[cfg(feature = "prost")]
|
||||||
mod prost;
|
mod prost;
|
||||||
|
|
||||||
#[cfg(all(test, feature = "data-prost"))]
|
|
||||||
mod prost_tests;
|
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
pub use self::decode::Streaming;
|
pub use self::decode::Streaming;
|
||||||
pub(crate) use self::encode::{encode_client, encode_server};
|
pub(crate) use self::encode::{encode_client, encode_server};
|
||||||
#[cfg(feature = "data-prost")]
|
#[cfg(feature = "prost")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "data-prost")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "prost")))]
|
||||||
pub use self::prost::ProstCodec;
|
pub use self::prost::ProstCodec;
|
||||||
use crate::Status;
|
use crate::Status;
|
||||||
pub use buffer::{DecodeBuf, EncodeBuf};
|
pub use buffer::{DecodeBuf, EncodeBuf};
|
||||||
|
|||||||
+156
-2
@@ -1,7 +1,7 @@
|
|||||||
use super::{Codec, DecodeBuf, Decoder, Encoder};
|
use super::{Codec, DecodeBuf, Decoder, Encoder};
|
||||||
use crate::codec::EncodeBuf;
|
use crate::codec::EncodeBuf;
|
||||||
use crate::{Code, Status};
|
use crate::{Code, Status};
|
||||||
use prost::Message;
|
use prost1::Message;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
/// A [`Codec`] that implements `application/grpc+proto` via the prost library..
|
/// 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
|
// Map Protobuf parse errors to an INTERNAL status code, as per
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
|
// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
|
||||||
Status::new(Code::Internal, error.to_string())
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user