Skip to main content

Module urls

Module urls 

Source
Expand description

Named URL reversal — Django’s reverse(name, params) + get_absolute_url(). Pair with register_url!. Issue #8. Named URL reversal — Django’s reverse() + get_absolute_url(). Issue #8.

Routes can be registered with a stable name at module-load time via the register_url! macro, then resolved back to a URL string through [reverse]. The shape mirrors Django’s URL conf:

ⓘ
use rustango::register_url;
use rustango::urls::reverse;
use std::collections::HashMap;

// At module scope — registers globally via `inventory`.
register_url!("post-detail", "/posts/{id}");
register_url!("home", "/");

// Anywhere at runtime.
let url = reverse("home", &HashMap::new()).unwrap();
assert_eq!(url, "/");

let mut p = HashMap::new();
p.insert("id", "42".to_owned());
let url = reverse("post-detail", &p).unwrap();
assert_eq!(url, "/posts/42");

Path-template placeholders use axum 0.8’s {name} shape. Param values are percent-encoded on substitution so caller-supplied values are safe even when they contain / / ? / #. Missing or extra params return a [ReverseError] at call time — there’s no compile-time check today (would require either codegen or a shared sentinel; queued as a future enhancement).

Manual sync with axum routes: register_url! only registers the pattern string with the reverse-lookup table — it does NOT mount a route on any axum Router. Callers must keep their axum routing in sync with their register_url! calls. A common shape is to put both side-by-side in src/urls.rs:

ⓘ
register_url!("post-detail", "/posts/{id}");
pub fn router() -> axum::Router {
    axum::Router::new().route("/posts/{id}", get(post_detail_view))
}

Duplicate names: two register_url!("name", ...) calls in the same binary collide silently — reverse picks the first match it finds in the inventory. Call [duplicates] at boot to surface any name registered more than once. Future work: a startup validator that turns this into a clean error before the server binds.

§Namespaced reverse (Django parity #380)

Django’s reverse("app:detail") form works today as a naming convention — register routes with colon-separated names and they look up just like any other key:

ⓘ
register_url!("posts:detail", "/posts/{id}");
register_url!("users:detail", "/users/{id}");

let url = reverse("posts:detail", &p).unwrap(); // → "/posts/42"

Rustango has no include() concept (every register_url! runs at module-load and lands in the global registry via inventory), so the colon prefix is part of the registered name rather than an auto-applied namespace. The behavior matches Django’s reverse("app:detail") API surface.

Structs§

NamedRoute
A named route — pattern + stable name. Registered at static-init time via [register_url!] (which calls inventory::submit! under the hood) and looked up by reverse at runtime.

Enums§

ReverseError
Failure modes for reverse.

Functions§

all_routes
Snapshot of the route registry — every entry registered via [register_url!] across every loaded module. Useful for boot-time diagnostics (see duplicates) or admin endpoints that surface the URL map.
duplicates
List every name that’s been registered more than once. Empty vec on a clean registry. Call at app boot — duplicates surface as silent “first wins” otherwise.
escape_leading_slashes
Django-parity django.utils.http.escape_leading_slashes(url) — escape leading slashes / backslashes on a URL fragment to prevent open-redirect attacks via protocol-relative URLs.
is_absolute_url
true when url looks absolute — it carries a scheme prefix (https: / mailto: / javascript: / etc.) OR starts with // (protocol-relative).
is_relative_url
true when url is path-relative (the inverse of is_absolute_url). Useful as a positive-frame predicate:
parse_query_pairs
Parse a querystring (with or without the leading ?) into a list of (key, value) pairs preserving original order. Malformed pairs (no =, multiple =) are passed through with empty / first- =-split values — same loose interpretation as browsers.
parse_query_pairs_grouped
Django-parity urllib.parse.parse_qs(qs) — parse a query string into a HashMap<String, Vec<String>>. Same value as parse_query_pairs but with multi-value keys collected into a vec per key (the canonical “give me everything for key X” shape).
register_querystring_filter
Register the Django 5.1 {% querystring %} equivalent as a Tera filter. Issue #19. Takes the current querystring as input and returns a new one with the given overrides applied.
register_url_tag
Register Django’s {% url %} equivalent as a Tera function on tera. Call this once at app setup alongside any other Tera configuration. Issue #14.
reverse
Resolve a registered name + parameters into a concrete URL string.
reverse_owned
Same as reverse but with String keys — convenience for the JSON / template-tag path where keys come from dynamic input.
url_has_allowed_host_and_scheme
Django-parity url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False) — returns true only when url is safe to redirect to from a trusted handler (e.g. the ?next=/path parameter on LoginView / LogoutView). Defends against the open-redirect vector where attacker-controlled input lands in a Location: header pointing at an external site.