Comments (7)
I intended this use-case to be handled via wrapping the return type of the service method with net::response::TryWithRaw
which will give the raw Hyper response including status code and headers. The body is accessible too but it may have been partially read by the deserialization process. But this does necessitate leaking this information to the user, or concealing your service trait, which is not what I'd like.
I think to support this properly, there needs to be a way to map the result from within the service method body, and set a different error type for Request
, so you can get the raw response and then package it up into your own error. I'll have to mull over this.
from anterofit.
Thank you for the suggestion, I'll give a try to TryWithRaw
for now. That also solves the problem of accessing the header of the response (wether there is an error or not), which is necessary to handle paging in the github api.
from anterofit.
I tried something like this:
#[macro_use]
extern crate anterofit;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use anterofit::{Request, JsonAdapter};
use anterofit::net::response::{TryWithRaw};
use serde::{Deserialize};
#[derive(Debug, Deserialize)]
pub struct Foo;
#[derive(Debug)]
pub struct User {
adapter: JsonAdapter,
}
service! {
pub trait MyService {
fn try_get_foo(&self) -> TryWithRaw<Foo> {
GET("")
}
}
impl for User{
|this| &this.adapter
}
impl[T] for T [where T: AsRef<User>] {
|this| &this.as_ref().adapter
}
}
impl User {
pub fn get_foo(&self) -> Request<Foo> {
self.try_get_foo().on_result(|res| {
match res {
Ok(TryWithRaw { raw: response, result: Ok(res) }) => {
Ok(res)
},
Ok(TryWithRaw { raw: response, result: Err(e) }) => {
Err("Something went wrong while getting foo".to_string())
},
Err(e) => {
Err("Something went really wrong while getting foo".to_string())
},
}
})
}
}
With this, I was trying to get a Result<Foo, String>
, but this does not work because on_result must return an anterofit::Result<T>
which is an alias (do we say alias in Rust?) for Result<T, anterofit::error::Error
>. So I cannot really use my own errors with this.
Is this what you meant by "wrapping the return type of the service method with net::response::TryWithRaw
" ?
I tried to wrap Request
into my own type, something like:
use anterofit;
use anterofit::net::Call;
pub struct Request<'a, T=()>(anterofit::Request<'a, T>);
impl<'a, T> Request<'a, T> {
pub fn new(req: anterofit::Request) -> Self {
Request(req)
}
pub fn immediate(res: anterofit::Result<T>) -> Request<'static, T> {
Request(anterofit::Request::immediate(res))
}
pub fn exec_here(self) -> Result<T, String> {
self.0.exec_here().map_err(|e| "my error".to_string()
}
pub fn is_immediate(&self) -> bool {
self.0.is_immediate()
}
}
impl<'a, T> Request<'a, T> where T: Send + 'static {
pub fn exec(self) -> Call<T> {
self.0.exec()
}
pub fn on_complete<F, R>(self, on_complete: F) -> Request<'a, R>
where F: FnOnce(T) -> R + Send + 'static, R: Send + 'static
{
self.on_result(|res| res.map(on_complete))
}
// this does not work, since self.0.on_result signature does not match
pub fn on_result<F, R>(self, on_result: F) -> Request<'a, R>
where F: FnOnce(anterofit::Result<T>) -> Result<R, String> + Send + 'static, R: Send + 'staticV
Request(self.0.on_result(on_result))
}
}
But:
- don't I need to wrap
anterfit::net::Call
too ? - I could not figure out how to handle
on_result
from anterofit.
I've got kind-of a plan to support this. On the parse-generics
branch, I'm experimenting with a syntax for forcing a full return type using -!>
instead of ->
, and then I have an idea to add a couple macros that let you override some of the logic in the generated service method, but the concept is far from complete. Feedback and input are most definitely welcome.
from anterofit.
What do you mean by "full return type" exactly ?
Since you're taking suggestions, I have one although it's unrelated to error handling.
Currently, for requests that take query parameters, I do something like this:
#[derive(Default, Debug)]
pub struct GetNotificationsParams {
pub all: Option<bool>,
pub participating: Option<bool>,
pub since: Option<DateTime<UTC>>,
pub before: Option<DateTime<UTC>>,
pub page: Option<u32>,
pub per_page: Option<u32>,
}
service! {
pub trait RootService {
fn get_notifications(&self, opts: Option<GetNotificationsParams>) -> TryWithRaw<Vec<Thread>> {
GET("notifications");
|mut builder| {
if let Some(ref opts) = opts {
if let Some(all) = opts.all {
builder.head_mut().query(&[("all", &all)]);
}
if let Some(participating) = opts.participating {
builder.head_mut().query(&[("participating", &participating)]);
}
if let Some(since) = opts.since {
builder.head_mut().query(&[("since", since.to_rfc3339())]);
}
if let Some(before) = opts.before {
builder.head_mut().query(&[("before", before.to_rfc3339())]);
}
if let Some(page) = opts.page {
builder.head_mut().query(&[("page", &page)]);
}
if let Some(per_page) = opts.per_page {
builder.head_mut().query(&[("per_page", &per_page)]);
}
}
Ok(builder)
}
}
}
}
This works well, but the caller always has to pass the extra argument:
// if no query parameter
root.get_notifications(None)
// or with some parameters
root.get_notification(Some(GetNotificationsParams {
//
}))
It would be nice if we coud just write:
// if not query parameter
root.get_notifications()
// or if we want to set a few query parameters
root.get_notifications().set_page(2).set_participating(true)
That would require the service methods to return some kind of RequestBuilder
though, which we would need to build into Request
.
I don't know how realistic this is, but since you asked for suggestions here is one :)
from anterofit.
So currently, when you define a service method and declare some return type T
, it gets rewritten as Request<T>
:
service! {
pub trait MyService {
fn get_api_version(&self) -> String {
GET!("/version")
}
}
}
// becomes
pub trait MyService {
fn get_api_version(&self) -> ::anterofit::Request<String> {
// ...
}
}
Using -!>
to declare a return type would override this, e.g.:
pub type MyRequest<T> = ::anterofit::Request<T>;
service! {
pub trait MyService {
fn get_api_version(&self) -!> MyRequest<String> {
GET!("/version")
}
}
}
// becomes
pub trait MyService {
fn get_api_version(&self) -> MyRequest<String> {
// ...
}
}
I'm still not sure on the exact token I want to use; I wanted something that draws attention to itself but my current choice could be confused with -> !
, which is used to designate a diverging function (one that will never return, e.g. panics or aborts or loops forever). Maybe =>
or -=>
but they seem too subtle.
I hadn't considered the use case you mentioned but I can see its utility. You could possibly do something like this by overriding the return type as shown above and short-circuiting the conversion from RequestBuilder
to Request
somehow. I've got a similar use-case in mind for allowing mapping the request result within a service method definition (as opposed to calliing Request::on_result()
later) but the concept isn't fleshed out yet.
from anterofit.
Fwiw I like the =>
version, even though it's subtle.
from anterofit.
Related Issues (14)
- Unanswered Design Questions
- generic service traits HOT 2
- Support of serde v1.x.x HOT 5
- Help with a strange API HOT 4
- map_response!() combinator HOT 1
- https does not work out of the box. HOT 3
- Base URL doesn’t do what it’s supposed to
- Drop rustc-serialize support HOT 4
- Is the project dead? HOT 1
- Serde compile times HOT 2
- Consider implementing OpenAPI (Swagger) specification HOT 4
- Complile problem with Hyper 0.10.5 using adapter.client(client) to set a proxy HOT 3
- Support Hyper 0.10 HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from anterofit.