Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it more ergonomic to return request builders #258

Open
KodrAus opened this issue Oct 14, 2017 · 1 comment
Open

Make it more ergonomic to return request builders #258

KodrAus opened this issue Oct 14, 2017 · 1 comment
Labels
Milestone

Comments

@KodrAus
Copy link
Member

KodrAus commented Oct 14, 2017

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?
@KodrAus
Copy link
Member Author

KodrAus commented Oct 14, 2017

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant