Skip to content

Commit

Permalink
feat: add OptionalParamSegment (closes #2896) (#3140)
Browse files Browse the repository at this point in the history
  • Loading branch information
gbj authored Oct 22, 2024
1 parent 7904e0c commit d0ef7b9
Show file tree
Hide file tree
Showing 20 changed files with 537 additions and 272 deletions.
6 changes: 2 additions & 4 deletions examples/hackernews/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod routes;
use leptos_meta::{provide_meta_context, Link, Meta, Stylesheet};
use leptos_router::{
components::{FlatRoutes, Route, Router, RoutingProgress},
ParamSegment, StaticSegment,
OptionalParamSegment, ParamSegment, StaticSegment,
};
use routes::{nav::*, stories::*, story::*, users::*};
use std::time::Duration;
Expand All @@ -28,9 +28,7 @@ pub fn App() -> impl IntoView {
<FlatRoutes fallback=|| "Not found.">
<Route path=(StaticSegment("users"), ParamSegment("id")) view=User/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view=Story/>
<Route path=ParamSegment("stories") view=Stories/>
// TODO allow optional params without duplication
<Route path=StaticSegment("") view=Stories/>
<Route path=OptionalParamSegment("stories") view=Stories/>
</FlatRoutes>
</main>
</Router>
Expand Down
6 changes: 2 additions & 4 deletions examples/hackernews_axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod routes;
use leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};
use leptos_router::{
components::{FlatRoutes, Route, Router, RoutingProgress},
ParamSegment, StaticSegment,
OptionalParamSegment, ParamSegment, StaticSegment,
};
use routes::{nav::*, stories::*, story::*, users::*};
use std::time::Duration;
Expand Down Expand Up @@ -46,9 +46,7 @@ pub fn App() -> impl IntoView {
<FlatRoutes fallback=|| "Not found.">
<Route path=(StaticSegment("users"), ParamSegment("id")) view=User/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view=Story/>
<Route path=ParamSegment("stories") view=Stories/>
// TODO allow optional params without duplication
<Route path=StaticSegment("") view=Stories/>
<Route path=OptionalParamSegment("stories") view=Stories/>
</FlatRoutes>
</main>
</Router>
Expand Down
6 changes: 2 additions & 4 deletions examples/hackernews_islands_axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod routes;
use leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};
use leptos_router::{
components::{FlatRoutes, Route, Router},
ParamSegment, StaticSegment,
OptionalParamSegment, ParamSegment, StaticSegment,
};
use routes::{nav::*, stories::*, story::*, users::*};
#[cfg(feature = "ssr")]
Expand Down Expand Up @@ -42,9 +42,7 @@ pub fn App() -> impl IntoView {
<FlatRoutes fallback=|| "Not found.">
<Route path=(StaticSegment("users"), ParamSegment("id")) view=User/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view=Story/>
<Route path=ParamSegment("stories") view=Stories/>
// TODO allow optional params without duplication
<Route path=StaticSegment("") view=Stories/>
<Route path=OptionalParamSegment("stories") view=Stories/>
</FlatRoutes>
</main>
</Router>
Expand Down
6 changes: 2 additions & 4 deletions examples/hackernews_js_fetch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod routes;
use leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};
use leptos_router::{
components::{FlatRoutes, Route, Router, RoutingProgress},
ParamSegment, StaticSegment,
OptionalParamSegment, ParamSegment, StaticSegment,
};
use routes::{nav::*, stories::*, story::*, users::*};
use std::time::Duration;
Expand Down Expand Up @@ -46,9 +46,7 @@ pub fn App() -> impl IntoView {
<FlatRoutes fallback=|| "Not found.">
<Route path=(StaticSegment("users"), ParamSegment("id")) view=User/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view=Story/>
<Route path=ParamSegment("stories") view=Stories/>
// TODO allow optional params without duplication
<Route path=StaticSegment("") view=Stories/>
<Route path=OptionalParamSegment("stories") view=Stories/>
</FlatRoutes>
</main>
</Router>
Expand Down
59 changes: 39 additions & 20 deletions integrations/actix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use leptos_router::{
components::provide_server_redirect,
location::RequestUrl,
static_routes::{RegenerationFn, ResolvedStaticPath},
Method, PathSegment, RouteList, RouteListing, SsrMode,
ExpandOptionals, Method, PathSegment, RouteList, RouteListing, SsrMode,
};
use once_cell::sync::Lazy;
use parking_lot::RwLock;
Expand Down Expand Up @@ -901,7 +901,7 @@ trait ActixPath {
fn to_actix_path(&self) -> String;
}

impl ActixPath for &[PathSegment] {
impl ActixPath for Vec<PathSegment> {
fn to_actix_path(&self) -> String {
let mut path = String::new();
for segment in self.iter() {
Expand All @@ -923,6 +923,14 @@ impl ActixPath for &[PathSegment] {
path.push_str(":.*}");
}
PathSegment::Unit => {}
PathSegment::OptionalParam(_) => {
#[cfg(feature = "tracing")]
tracing::error!(
"to_axum_path should only be called on expanded \
paths, which do not have OptionalParam any longer"
);
Default::default()
}
}
}
path
Expand All @@ -938,23 +946,34 @@ pub struct ActixRouteListing {
regenerate: Vec<RegenerationFn>,
}

impl From<RouteListing> for ActixRouteListing {
fn from(value: RouteListing) -> Self {
let path = value.path().to_actix_path();
let path = if path.is_empty() {
"/".to_string()
} else {
path
};
let mode = value.mode();
let methods = value.methods().collect();
let regenerate = value.regenerate().into();
Self {
path,
mode: mode.clone(),
methods,
regenerate,
}
trait IntoRouteListing: Sized {
fn into_route_listing(self) -> Vec<ActixRouteListing>;
}

impl IntoRouteListing for RouteListing {
fn into_route_listing(self) -> Vec<ActixRouteListing> {
self.path()
.to_vec()
.expand_optionals()
.into_iter()
.map(|path| {
let path = path.to_actix_path();
let path = if path.is_empty() {
"/".to_string()
} else {
path
};
let mode = self.mode();
let methods = self.methods().collect();
let regenerate = self.regenerate().into();
ActixRouteListing {
path,
mode: mode.clone(),
methods,
regenerate,
}
})
.collect()
}
}

Expand Down Expand Up @@ -1033,7 +1052,7 @@ where
let mut routes = routes
.into_inner()
.into_iter()
.map(ActixRouteListing::from)
.flat_map(IntoRouteListing::into_route_listing)
.collect::<Vec<_>>();

(
Expand Down
59 changes: 39 additions & 20 deletions integrations/axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use leptos_router::{
components::provide_server_redirect,
location::RequestUrl,
static_routes::{RegenerationFn, StaticParamsMap},
PathSegment, RouteList, RouteListing, SsrMode,
ExpandOptionals, PathSegment, RouteList, RouteListing, SsrMode,
};
#[cfg(feature = "default")]
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -1267,23 +1267,34 @@ pub struct AxumRouteListing {
regenerate: Vec<RegenerationFn>,
}

impl From<RouteListing> for AxumRouteListing {
fn from(value: RouteListing) -> Self {
let path = value.path().to_axum_path();
let path = if path.is_empty() {
"/".to_string()
} else {
path
};
let mode = value.mode();
let methods = value.methods().collect();
let regenerate = value.regenerate().into();
Self {
path,
mode: mode.clone(),
methods,
regenerate,
}
trait IntoRouteListing: Sized {
fn into_route_listing(self) -> Vec<AxumRouteListing>;
}

impl IntoRouteListing for RouteListing {
fn into_route_listing(self) -> Vec<AxumRouteListing> {
self.path()
.to_vec()
.expand_optionals()
.into_iter()
.map(|path| {
let path = path.to_axum_path();
let path = if path.is_empty() {
"/".to_string()
} else {
path
};
let mode = self.mode();
let methods = self.methods().collect();
let regenerate = self.regenerate().into();
AxumRouteListing {
path,
mode: mode.clone(),
methods,
regenerate,
}
})
.collect()
}
}

Expand Down Expand Up @@ -1367,7 +1378,7 @@ where
let mut routes = routes
.into_inner()
.into_iter()
.map(AxumRouteListing::from)
.flat_map(IntoRouteListing::into_route_listing)
.collect::<Vec<_>>();

(
Expand Down Expand Up @@ -1700,7 +1711,7 @@ trait AxumPath {
fn to_axum_path(&self) -> String;
}

impl AxumPath for &[PathSegment] {
impl AxumPath for Vec<PathSegment> {
fn to_axum_path(&self) -> String {
let mut path = String::new();
for segment in self.iter() {
Expand All @@ -1720,6 +1731,14 @@ impl AxumPath for &[PathSegment] {
path.push_str(s);
}
PathSegment::Unit => {}
PathSegment::OptionalParam(_) => {
#[cfg(feature = "tracing")]
tracing::error!(
"to_axum_path should only be called on expanded \
paths, which do not have OptionalParam any longer"
);
Default::default()
}
}
}
path
Expand Down
6 changes: 3 additions & 3 deletions router/src/flat_router.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::{
hooks::Matched,
location::{LocationProvider, Url},
matching::Routes,
matching::{MatchParams, Routes},
params::ParamsMap,
view_transition::start_view_transition,
ChooseView, MatchInterface, MatchNestedRoutes, MatchParams, PathSegment,
RouteList, RouteListing, RouteMatchId,
ChooseView, MatchInterface, MatchNestedRoutes, PathSegment, RouteList,
RouteListing, RouteMatchId,
};
use any_spawner::Executor;
use either_of::Either;
Expand Down
8 changes: 2 additions & 6 deletions router/src/matching/horizontal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::{PartialPathMatch, PathSegment};
use std::borrow::Cow;
mod param_segments;
mod static_segment;
mod tuples;
Expand All @@ -13,12 +12,9 @@ pub use static_segment::*;
/// as subsequent segments of the URL and tries to match them all. For a "vertical"
/// matching that sees a tuple as alternatives to one another, see [`RouteChild`](super::RouteChild).
pub trait PossibleRouteMatch {
type ParamsIter: IntoIterator<Item = (Cow<'static, str>, String)>;
const OPTIONAL: bool = false;

fn test<'a>(
&self,
path: &'a str,
) -> Option<PartialPathMatch<'a, Self::ParamsIter>>;
fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>>;

fn generate_path(&self, path: &mut Vec<PathSegment>);
}
Loading

0 comments on commit d0ef7b9

Please sign in to comment.