elastic
                                
                                 elastic copied to clipboard
                                
                                    elastic copied to clipboard
                            
                            
                            
                        Make it more ergonomic to return request builders
Motivation
Right now, calls to client.search, or client.get_document etc will return a response builder with a few generic parameters like:
SearchRequestBuilder<TSender, TDocument, TBody>
This isn't very nice to include in a function signature.
Single traits
Design
Offer two traits SyncRequest and AsyncRequest that are implemented for many request builders and can be used to erase 'implementation detail' generics from request builders when they're returned from methods.
trait SyncRequest<TResponse> {
    fn send(self) -> Result<TResponse, Error>;
}
trait AsyncRequest<TResponse> {
    type Future: Future<Item = TResponse, Error = Error>;
    fn send(self) -> Self::Future;
}
// Returning an asynchronous search request
fn my_method() -> impl AsyncRequest<SearchResponse<MyType>>;
Multiple traits
Design
One idea is to use traits and impl Trait (or Box) to erase the type:
trait SyncSearchRequest<TDocument> {
    fn send(self) -> Result<SearchResponse<TDocument>>;
}
trait AsyncSearchRequest<TDocument> {
    fn send(self) -> Pending<TDocument>;
}
impl<TDocument, TBody> SyncSearchRequest<TDocument> for SearchRequestBuilder<SyncSender, TDocument, TBody> {
    fn send(self) -> Result<SearchResponse<TDocument>> {
        (self as SearchRequestBuilder<SyncSender, TDocument, TBody>).send()
    }
}
Using traits has a benefit over type definitions in that we can effectively erase generic parameters through a blanket implementation (TBody doesn't appear in the SearchRequest trait).
Open questions
- Is this intuitive enough? We'd need to add an example for returning request builders from methods
- What about raw requests?
- What if the trait name conflicts with an endpoint type?
- Could we leverage traits better for these things?
- Is this too much extra boilerplate for request methods?
- Should these go in the prelude or not?
Another option is an even more generic trait:
trait SyncRequest<TResponse> {
    fn send(self) -> Result<TResponse, Error>;
}
trait AsyncRequest<TResponse> {
    type Future: Future<Item = TResponse, Error = Error>;
    fn send(self) -> Self::Future;
}
// Returning an asynchronous search request
fn my_method() -> impl AsyncRequest<SearchResponse<MyType>>;
In the future, if impl Trait supported type definitions then we could have aliases for these traits. I think this is probably a better approach than above because it's less code, but means callers need to have a better idea of what type to return. I think we could work around this with examples and docs.