Repository: seanmonstar/warp (language: rust)

Issue:
Optional query parameters seem either very convoluted or conceringly missing
I've found issues in the past referencing how to possibly get this done like #86; however, this seems like an incredibly heavy-handed and honestly hacky solution to do something fairly standard in most web frameworks I've dealt with. The original author of that issue even pointed out that it seemed too complicated, but it was closed and moved on. 

This becomes especially increasingly frustrating as you add more optional query parameters and your code forks based on how they are provided; i.e. why should I have to create 5 different handler functions based on the parameters supplied when I can just have one that figures out what to do with the parameters supplied? 

Why can't we just create an endpoint such as `/foo` with optional query parameters `bar` and `baz` as such:
```
warp::path("foo").and(warp::query())
        .map(|options: Option<QueryOptions>| match names {
            Some(opt) => format!("bar: {}, baz: {}", opt.bar, opt.baz),
            None => "no query params provided".to_owned(),
        })
```

Thinking of an instance where I have some grouping of optional query parameters, like for example, pagination query parameters. If I supply `size`, I would like to also enforce that `page_number` or something else be provided as well. Thus trying to group everything together under something like `QueryOptions` instead of just trying to figure out what to do if someone provides only `size` or only some other thing and figuring out how to proceed for each of those. 

Per the title, I can't tell if this is just missing support in warp right now, or this is more complicated than I think it reasonably should be to figure out.

Thanks



Discussion / hints:
Can't what you wrote be done already? If I'm mistaken (probably), we might need to add a `query::optional()` filter. If it is possible already, then we should really add such an example to the docs.
Actually I think you're right, haven't tried it myself yet since originally posting, but I think you should be able to with serde's support for struct flattening: https://serde.rs/attr-flatten.html (they even used my example specifically haha)

I agree though that at the very least might be worth adding an example of supporting optional query parameters in the docs, at least in the interim while you+others decide if implementing a `query::optional()` filter should go forward. Probably especially good to start with docs since the docs for the `warp::filters::query` module doesn't have any examples at all.
Just be sure I understood, we are talking about something different than suggested in this example, correct?

https://github.com/seanmonstar/warp/blob/8377282edd079451e43616c990317c2a9671c41f/examples/todos.rs#L54
Oh, thanks for pointing that out! Indeed, it does already work. So then, we could probably just show a simplified version in the docs.
@apiraino pretty much, the example I was originally asking about is a tiny bit more complicated, but I think it's all serde at that point and unrelated to warp. 

Simplified version in the docs I think would be perfect

Fix patch:
diff --git a/src/filters/query.rs b/src/filters/query.rs
index b31f096f3..a2a766366 100644
--- a/src/filters/query.rs
+++ b/src/filters/query.rs
@@ -10,6 +10,58 @@ use crate::reject::{self, Rejection};
 /// Creates a `Filter` that decodes query parameters to the type `T`.
 ///
 /// If cannot decode into a `T`, the request is rejected with a `400 Bad Request`.
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+/// use warp::{
+///     http::Response,
+///     Filter,
+/// };
+///
+/// let route = warp::any()
+///     .and(warp::query::<HashMap<String, String>>())
+///     .map(|map: HashMap<String, String>| {
+///         let mut response: Vec<String> = Vec::new();
+///         for (key, value) in map.into_iter() {
+///             response.push(format!("{}={}", key, value))
+///         }
+///         Response::builder().body(response.join(";"))
+///     });
+/// ```
+///
+/// You can define your custom query object and deserialize with [Serde][Serde]. Ensure to include
+/// the crate in your dependencies before usage.
+///
+/// ```
+/// use serde_derive::{Deserialize, Serialize};
+/// use std::collections::HashMap;
+/// use warp::{
+///     http::Response,
+///     Filter,
+/// };
+///
+/// #[derive(Serialize, Deserialize)]
+/// struct FooQuery {
+///     foo: Option<String>,
+///     bar: u8,
+/// }
+///
+/// let route = warp::any()
+///     .and(warp::query::<FooQuery>())
+///     .map(|q: FooQuery| {
+///         if let Some(foo) = q.foo {
+///             Response::builder().body(format!("foo={}", foo))
+///         } else {
+///             Response::builder().body(format!("bar={}", q.bar))
+///         }
+///     });
+/// ```
+///
+/// For more examples, please take a look at [examples/query_string.rs](https://github.com/seanmonstar/warp/blob/master/examples/query_string.rs).
+///
+/// [Serde]: https://docs.rs/serde
 pub fn query<T: DeserializeOwned + Send + 'static>(
 ) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
     filter_fn_one(|route| {


Test patch: