Router routes with query segments serialize a trailing `?` even when the query is empty
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 84/100
Research direction
Start in packages/router-macro/src/query.rs, where the query writer is described as emitting the separator before rendering the field output. Run the supplied reproduction covering the empty spread segment and the None option; done means both serialize without a trailing ?, while non-empty queries still include it.
Written by the indexing model from the issue text.
Description
When a route has a query segment, its Display impl always writes the ?, even when nothing follows it. With a spread segment (?:..params) and a params struct whose Display renders empty, the route serializes as /translations?, which then shows up in the address bar and in copied links. The same happens with a named segment on an Option field: #[route("/reset?:token")] with token: None gives /reset?, even though the None pair itself is correctly elided.
As best I can tell the ? is written unconditionally in the router macro's query writer (packages/router-macro/src/query.rs), before the field's rendered output is known, and the same code is on main. It looks like an oversight rather than a design choice, since the pair elision (skipping None pairs and their & separators) is already there.
Repro on 0.7.10, with dioxus = { version = "=0.7.10", features = ["router"] }:
use dioxus::prelude::*;
use dioxus::router::FromQuery;
use std::fmt::{self, Display, Formatter};
#[derive(Debug, Clone, PartialEq, Default)]
struct Params {
search: Option<String>,
}
impl Display for Params {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match &self.search {
Some(search) => write!(f, "search={search}"),
None => Ok(()),
}
}
}
impl FromQuery for Params {
fn from_query(query: &str) -> Self {
let search = query
.split('&')
.filter_map(|segment| segment.split_once('='))
.find(|(key, _)| *key == "search")
.map(|(_, value)| value.to_owned())
.filter(|value| !value.is_empty());
Self { search }
}
}
#[derive(Debug, Clone, PartialEq, Routable)]
enum Route {
#[route("/translations?:..params")]
Translations { params: Params },
#[route("/reset?:token")]
Reset { token: Option<String> },
}
#[component]
fn Translations(params: Params) -> Element {
rsx! {}
}
#[component]
fn Reset(token: Option<String>) -> Element {
rsx! {}
}
fn main() {
let empty = Route::Translations { params: Params::default() };
let filled = Route::Translations {
params: Params { search: Some("zone".to_owned()) },
};
let reset_none = Route::Reset { token: None };
println!("{empty}"); // /translations?
println!("{filled}"); // /translations?search=zone
println!("{reset_none}"); // /reset?
}
Expected: /translations and /reset. Actual: /translations? and /reset?.
Cosmetic, but visible on every navigation.
- Dominant language
- Rust
- Stars
- 39.2k
- Forks
- 1.9k
- Avg merge
- 3d 12h
- Merged PRs (30d)
- 6
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from DioxusLabs/dioxus
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
DioxusLabs/dioxus#5835 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
DioxusLabs/dioxus#5821 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
DioxusLabs/dioxus#5744 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DioxusLabs/dioxus#5710 ·
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DioxusLabs/dioxus#5709 · 1 reaction ·
All issues in DioxusLabs/dioxus
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
gitbutlerapp/gitbutler#15998 · 1 comment ·
-
bug triage:deciding
Difficulty 1/5 Under an hour Newbie friendliness 88/100
open-telemetry/otel-arrow#4132 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100