{"action":"created","issue":{"url":"https://api.github.com/repos/tower-rs/tower-grpc/issues/64","repository_url":"https://api.github.com/repos/tower-rs/tower-grpc","labels_url":"https://api.github.com/repos/tower-rs/tower-grpc/issues/64/labels{/name}","comments_url":"https://api.github.com/repos/tower-rs/tower-grpc/issues/64/comments","events_url":"https://api.github.com/repos/tower-rs/tower-grpc/issues/64/events","html_url":"https://github.com/tower-rs/tower-grpc/issues/64","id":323457475,"node_id":"MDU6SXNzdWUzMjM0NTc0NzU=","number":64,"title":"Common trait for Client and Server","user":{"login":"olix0r","id":240738,"node_id":"MDQ6VXNlcjI0MDczOA==","avatar_url":"https://avatars3.githubusercontent.com/u/240738?v=4","gravatar_id":"","url":"https://api.github.com/users/olix0r","html_url":"https://github.com/olix0r","followers_url":"https://api.github.com/users/olix0r/followers","following_url":"https://api.github.com/users/olix0r/following{/other_user}","gists_url":"https://api.github.com/users/olix0r/gists{/gist_id}","starred_url":"https://api.github.com/users/olix0r/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/olix0r/subscriptions","organizations_url":"https://api.github.com/users/olix0r/orgs","repos_url":"https://api.github.com/users/olix0r/repos","events_url":"https://api.github.com/users/olix0r/events{/privacy}","received_events_url":"https://api.github.com/users/olix0r/received_events","type":"User","site_admin":false},"labels":[{"id":770549461,"node_id":"MDU6TGFiZWw3NzA1NDk0NjE=","url":"https://api.github.com/repos/tower-rs/tower-grpc/labels/enhancement","name":"enhancement","color":"84b6eb","default":true}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":10,"created_at":"2018-05-16T03:35:35Z","updated_at":"2018-10-10T05:10:46Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"It's convenient to implement logic around a gRPC interface independently of whether the implementation is local or remote. This enables incremental decomposition and testability.\r\n\r\nSpecifically:\r\n\r\nIn generated code, I have something like:\r\n```rust\r\npub mod client {\r\n    #[derive(Debug)]\r\n    pub struct Iface<T> {\r\n        inner: grpc::Grpc<T>,\r\n    }\r\n\r\n    impl<T> Iface<T>\r\n    where T: tower_h2::HttpService,\r\n    {\r\n        pub fn new(inner: T) -> Self {\r\n            let inner = grpc::Grpc::new(inner);\r\n            Self { inner }\r\n        }\r\n\r\n        pub fn poll_ready(&mut self) -> futures::Poll<(), grpc::Error<T::Error>> {\r\n            self.inner.poll_ready()\r\n        }\r\n\r\n        pub fn get(&mut self, request: grpc::Request<Get>) -> grpc::server_streaming::ResponseFuture<Update, T::Future>\r\n        where grpc::unary::Once<Get>: grpc::Encodable<T::RequestBody>,\r\n        {\r\n            ...\r\n        }\r\n    }\r\n}\r\n```\r\n```rust\r\npub mod server {\r\n    ...\r\n\r\n    pub trait Iface: Clone {\r\n        type GetStream: futures::Stream<Item = Update, Error = grpc::Error>;\r\n        type GetFuture: futures::Future<Item = grpc::Response<Self::GetStream>, Error = grpc::Error>;\r\n\r\n        fn get(&mut self, request: grpc::Request<Get>) -> Self::GetFuture;\r\n    }\r\n\r\n   ...\r\n}\r\n```\r\n\r\nI propose that:\r\n* the `trait Iface` definition is moved from the `server` module to the parent, without the `Clone` bound.\r\n* `fn poll_ready` is added to the service trait\r\n* the `server` module defines `trait Iface: super::Iface + Clone;`\r\n* the `client` module provides an `impl super::Iface`\r\n\r\nIf this is done, code that interacts with grpc interfaces can be generic on `Iface` rather than the underlying Http service.  This makes it easy to implement an `Iface` in tests, for instance, to mock client behavior."},"comment":{"url":"https://api.github.com/repos/tower-rs/tower-grpc/issues/comments/428439952","html_url":"https://github.com/tower-rs/tower-grpc/issues/64#issuecomment-428439952","issue_url":"https://api.github.com/repos/tower-rs/tower-grpc/issues/64","id":428439952,"node_id":"MDEyOklzc3VlQ29tbWVudDQyODQzOTk1Mg==","user":{"login":"carllerche","id":6180,"node_id":"MDQ6VXNlcjYxODA=","avatar_url":"https://avatars3.githubusercontent.com/u/6180?v=4","gravatar_id":"","url":"https://api.github.com/users/carllerche","html_url":"https://github.com/carllerche","followers_url":"https://api.github.com/users/carllerche/followers","following_url":"https://api.github.com/users/carllerche/following{/other_user}","gists_url":"https://api.github.com/users/carllerche/gists{/gist_id}","starred_url":"https://api.github.com/users/carllerche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carllerche/subscriptions","organizations_url":"https://api.github.com/users/carllerche/orgs","repos_url":"https://api.github.com/users/carllerche/repos","events_url":"https://api.github.com/users/carllerche/events{/privacy}","received_events_url":"https://api.github.com/users/carllerche/received_events","type":"User","site_admin":false},"created_at":"2018-10-10T05:10:46Z","updated_at":"2018-10-10T05:10:46Z","author_association":"MEMBER","body":"I believe that this issue is an argument for https://github.com/tower-rs/tower/issues/99\r\n\r\nIn this case, we would not need an associated request body type for each gRPC method."}}
{"actor":{"display_login":"carllerche"}}