Skip to content

Commit b1c4746

Browse files
committed
refactor: moved router into core
This cleans up the engine significantly and removes the 'watertight' assurances of macro magic.
1 parent 33f439c commit b1c4746

File tree

7 files changed

+392
-315
lines changed

7 files changed

+392
-315
lines changed
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use crate::Html;
2+
use sycamore_router::Route;
3+
4+
use super::RouteVerdict;
5+
6+
/// Creates an app-specific routing `struct`. Sycamore expects an `enum` to do this, so we create a `struct` that behaves similarly. If
7+
/// we don't do this, we can't get the information necessary for routing into the `enum` at all (context and global variables don't suit
8+
/// this particular case).
9+
#[macro_export]
10+
macro_rules! create_app_route {
11+
{
12+
name => $name:ident,
13+
render_cfg => $render_cfg:expr,
14+
templates => $templates:expr,
15+
locales => $locales:expr
16+
} => {
17+
/// The route type for the app, with all routing logic inbuilt through the generation macro.
18+
struct $name<G: $crate::Html>($crate::internal::router::RouteVerdict<G>);
19+
impl<G: $crate::Html> $crate::internal::router::PerseusRoute<G> for $name<G> {
20+
fn get_verdict(&self) -> &$crate::internal::router::RouteVerdict<G> {
21+
&self.0
22+
}
23+
}
24+
impl<G: $crate::Html> ::sycamore_router::Route for $name<G> {
25+
fn match_route(path: &[&str]) -> Self {
26+
let verdict = $crate::internal::router::match_route(path, $render_cfg, $templates, $locales);
27+
Self(verdict)
28+
}
29+
}
30+
};
31+
}
32+
33+
/// A trait for the routes in Perseus apps. This should be used almost exclusively internally, and you should never need to touch
34+
/// it unless you're building a custom engine.
35+
pub trait PerseusRoute<G: Html>: Route {
36+
/// Gets the route verdict for the current route.
37+
fn get_verdict(&self) -> &RouteVerdict<G>;
38+
}

packages/perseus/src/router.rs renamed to packages/perseus/src/router/match_route.rs

+1-131
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1+
use super::{RouteInfo, RouteInfoAtomic, RouteVerdict, RouteVerdictAtomic};
12
use crate::i18n::Locales;
23
use crate::template::{ArcTemplateMap, Template, TemplateMap};
34
use crate::Html;
45
use std::collections::HashMap;
56
use std::rc::Rc;
6-
use sycamore::prelude::ReadSignal;
7-
use sycamore::prelude::Signal;
87

98
/// The backend for `get_template_for_path` to avoid code duplication for the `Arc` and `Rc` versions.
109
macro_rules! get_template_for_path {
@@ -211,132 +210,3 @@ pub fn match_route_atomic<'a, G: Html>(
211210

212211
verdict
213212
}
214-
215-
/// Information about a route, which, combined with error pages and a client-side translations manager, allows the initialization of
216-
/// the app shell and the rendering of a page.
217-
#[derive(Debug)]
218-
pub struct RouteInfo<G: Html> {
219-
/// The actual path of the route.
220-
pub path: String,
221-
/// The template that will be used. The app shell will derive props and a translator to pass to the template function.
222-
pub template: Rc<Template<G>>,
223-
/// Whether or not the matched page was incrementally-generated at runtime (if it has been yet). If this is `true`, the server will
224-
/// use a mutable store rather than an immutable one. See the book for more details.
225-
pub was_incremental_match: bool,
226-
/// The locale for the template to be rendered in.
227-
pub locale: String,
228-
}
229-
230-
/// The possible outcomes of matching a route. This is an alternative implementation of Sycamore's `Route` trait to enable greater
231-
/// control and tighter integration of routing with templates. This can only be used if `Routes` has been defined in context (done
232-
/// automatically by the CLI).
233-
#[derive(Debug)]
234-
pub enum RouteVerdict<G: Html> {
235-
/// The given route was found, and route information is attached.
236-
Found(RouteInfo<G>),
237-
/// The given route was not found, and a `404 Not Found` page should be shown.
238-
NotFound,
239-
/// The given route maps to the locale detector, which will redirect the user to the attached path (in the appropriate locale).
240-
LocaleDetection(String),
241-
}
242-
243-
/// Information about a route, which, combined with error pages and a client-side translations manager, allows the initialization of
244-
/// the app shell and the rendering of a page.
245-
///
246-
/// This version is designed for multithreaded scenarios, and stores a reference to a template rather than an `Rc<Template<G>>`. That means this is not compatible
247-
/// with Perseus on the client-side, only on the server-side.
248-
#[derive(Debug)]
249-
pub struct RouteInfoAtomic<'a, G: Html> {
250-
/// The actual path of the route.
251-
pub path: String,
252-
/// The template that will be used. The app shell will derive props and a translator to pass to the template function.
253-
pub template: &'a Template<G>,
254-
/// Whether or not the matched page was incrementally-generated at runtime (if it has been yet). If this is `true`, the server will
255-
/// use a mutable store rather than an immutable one. See the book for more details.
256-
pub was_incremental_match: bool,
257-
/// The locale for the template to be rendered in.
258-
pub locale: String,
259-
}
260-
261-
/// The possible outcomes of matching a route. This is an alternative implementation of Sycamore's `Route` trait to enable greater
262-
/// control and tighter integration of routing with templates. This can only be used if `Routes` has been defined in context (done
263-
/// automatically by the CLI).
264-
///
265-
/// This version uses `RouteInfoAtomic`, and is designed for multithreaded scenarios (i.e. on the server).
266-
#[derive(Debug)]
267-
pub enum RouteVerdictAtomic<'a, G: Html> {
268-
/// The given route was found, and route information is attached.
269-
Found(RouteInfoAtomic<'a, G>),
270-
/// The given route was not found, and a `404 Not Found` page should be shown.
271-
NotFound,
272-
/// The given route maps to the locale detector, which will redirect the user to the attached path (in the appropriate locale).
273-
LocaleDetection(String),
274-
}
275-
276-
/// Creates an app-specific routing `struct`. Sycamore expects an `enum` to do this, so we create a `struct` that behaves similarly. If
277-
/// we don't do this, we can't get the information necessary for routing into the `enum` at all (context and global variables don't suit
278-
/// this particular case).
279-
#[macro_export]
280-
macro_rules! create_app_route {
281-
{
282-
name => $name:ident,
283-
render_cfg => $render_cfg:expr,
284-
templates => $templates:expr,
285-
locales => $locales:expr
286-
} => {
287-
/// The route type for the app, with all routing logic inbuilt through the generation macro.
288-
struct $name<G: $crate::Html>($crate::internal::router::RouteVerdict<G>);
289-
impl<G: $crate::Html> ::sycamore_router::Route for $name<G> {
290-
fn match_route(path: &[&str]) -> Self {
291-
let verdict = $crate::internal::router::match_route(path, $render_cfg, $templates, $locales);
292-
Self(verdict)
293-
}
294-
}
295-
};
296-
}
297-
298-
/// The state for the router.
299-
#[derive(Clone, Debug)]
300-
pub struct RouterState {
301-
/// The router's current load state.
302-
load_state: Signal<RouterLoadState>,
303-
}
304-
impl Default for RouterState {
305-
/// Creates a default instance of the router state intended for server-side usage.
306-
fn default() -> Self {
307-
Self {
308-
load_state: Signal::new(RouterLoadState::Server),
309-
}
310-
}
311-
}
312-
impl RouterState {
313-
/// Gets the load state of the router. You'll still need to call `.get()` after this (this just returns a `ReadSignal` to derive other state from in a `create_memo` or the like).
314-
pub fn get_load_state(&self) -> ReadSignal<RouterLoadState> {
315-
self.load_state.handle()
316-
}
317-
/// Sets the load state of the router.
318-
pub fn set_load_state(&self, new: RouterLoadState) {
319-
self.load_state.set(new);
320-
}
321-
}
322-
323-
/// The current load state of the router. You can use this to be warned of when a new page is about to be loaded (and display a loading bar or the like, perhaps).
324-
#[derive(Clone, Debug)]
325-
pub enum RouterLoadState {
326-
/// The page has been loaded.
327-
Loaded {
328-
/// The name of the template being loaded (mostly for convenience).
329-
template_name: String,
330-
/// The full path to the new page being loaded (including the locale, if we're using i18n).
331-
path: String,
332-
},
333-
/// A new page is being loaded, and will soon replace whatever is currently loaded. The name of the new template is attached.
334-
Loading {
335-
/// The name of the template being loaded (mostly for convenience).
336-
template_name: String,
337-
/// The full path to the new page being loaded (including the locale, if we're using i18n).
338-
path: String,
339-
},
340-
/// We're on the server, and there is no router. Whatever you render based on this state will appear when the user first loads the page, before it's made interactive.
341-
Server,
342-
}

packages/perseus/src/router/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
mod app_route; // This just exposes a macro
2+
mod match_route;
3+
mod route_verdict;
4+
mod router_component;
5+
mod router_state;
6+
7+
pub use app_route::PerseusRoute;
8+
pub use match_route::{
9+
get_template_for_path, get_template_for_path_atomic, match_route, match_route_atomic,
10+
};
11+
pub use route_verdict::{RouteInfo, RouteInfoAtomic, RouteVerdict, RouteVerdictAtomic};
12+
pub use router_component::*; // TODO
13+
pub use router_state::{RouterLoadState, RouterState};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use crate::template::Template;
2+
use crate::Html;
3+
use std::rc::Rc;
4+
5+
/// Information about a route, which, combined with error pages and a client-side translations manager, allows the initialization of
6+
/// the app shell and the rendering of a page.
7+
#[derive(Debug)]
8+
pub struct RouteInfo<G: Html> {
9+
/// The actual path of the route.
10+
pub path: String,
11+
/// The template that will be used. The app shell will derive props and a translator to pass to the template function.
12+
pub template: Rc<Template<G>>,
13+
/// Whether or not the matched page was incrementally-generated at runtime (if it has been yet). If this is `true`, the server will
14+
/// use a mutable store rather than an immutable one. See the book for more details.
15+
pub was_incremental_match: bool,
16+
/// The locale for the template to be rendered in.
17+
pub locale: String,
18+
}
19+
20+
/// The possible outcomes of matching a route. This is an alternative implementation of Sycamore's `Route` trait to enable greater
21+
/// control and tighter integration of routing with templates. This can only be used if `Routes` has been defined in context (done
22+
/// automatically by the CLI).
23+
#[derive(Debug)]
24+
pub enum RouteVerdict<G: Html> {
25+
/// The given route was found, and route information is attached.
26+
Found(RouteInfo<G>),
27+
/// The given route was not found, and a `404 Not Found` page should be shown.
28+
NotFound,
29+
/// The given route maps to the locale detector, which will redirect the user to the attached path (in the appropriate locale).
30+
LocaleDetection(String),
31+
}
32+
33+
/// Information about a route, which, combined with error pages and a client-side translations manager, allows the initialization of
34+
/// the app shell and the rendering of a page.
35+
///
36+
/// This version is designed for multithreaded scenarios, and stores a reference to a template rather than an `Rc<Template<G>>`. That means this is not compatible
37+
/// with Perseus on the client-side, only on the server-side.
38+
#[derive(Debug)]
39+
pub struct RouteInfoAtomic<'a, G: Html> {
40+
/// The actual path of the route.
41+
pub path: String,
42+
/// The template that will be used. The app shell will derive props and a translator to pass to the template function.
43+
pub template: &'a Template<G>,
44+
/// Whether or not the matched page was incrementally-generated at runtime (if it has been yet). If this is `true`, the server will
45+
/// use a mutable store rather than an immutable one. See the book for more details.
46+
pub was_incremental_match: bool,
47+
/// The locale for the template to be rendered in.
48+
pub locale: String,
49+
}
50+
51+
/// The possible outcomes of matching a route. This is an alternative implementation of Sycamore's `Route` trait to enable greater
52+
/// control and tighter integration of routing with templates. This can only be used if `Routes` has been defined in context (done
53+
/// automatically by the CLI).
54+
///
55+
/// This version uses `RouteInfoAtomic`, and is designed for multithreaded scenarios (i.e. on the server).
56+
#[derive(Debug)]
57+
pub enum RouteVerdictAtomic<'a, G: Html> {
58+
/// The given route was found, and route information is attached.
59+
Found(RouteInfoAtomic<'a, G>),
60+
/// The given route was not found, and a `404 Not Found` page should be shown.
61+
NotFound,
62+
/// The given route maps to the locale detector, which will redirect the user to the attached path (in the appropriate locale).
63+
LocaleDetection(String),
64+
}

0 commit comments

Comments
 (0)