···1010pub mod content;
1111pub mod errors;
1212pub mod route;
1313-1414-mod routing;
1313+pub mod routing;
15141615// Exports for end-users
1716pub use build::metadata::{BuildOutput, PageOutput, StaticAssetOutput};
+77-67
examples/library/src/build.rs
···44 BuildOptions,
55 assets::RouteAssets,
66 content::ContentSources,
77- route::{DynamicRouteContext, FullRoute, PageContext, PageParams, RouteType},
77+ route::{DynamicRouteContext, FullRoute, PageContext, PageParams},
88+ routing::extract_params_from_raw_route,
89};
9101011pub fn build_website(
···1213 mut content_sources: ContentSources,
1314 options: &BuildOptions,
1415) -> Result<(), Box<dyn std::error::Error>> {
1515- // Initialize all the content sources;
1616+ // Initialize all the content sources
1617 content_sources.init_all();
17181819 // Options we'll be passing to RouteAssets instances.
···2021 let route_assets_options = options.route_assets_options();
21222223 // Create the assets directory if it doesn't exist.
2323- fs::create_dir_all(&route_assets_options.assets_dir)?;
2424+ fs::create_dir_all(&route_assets_options.output_assets_dir)?;
24252526 for route in routes {
2626- match route.route_type() {
2727- RouteType::Static => {
2828- // Our page does not include content or assets, but we'll set those up for future use.
2929- let mut page_assets = RouteAssets::new(&route_assets_options, None);
2727+ // Get the raw route path (e.g., "/articles/[slug]")
2828+ let Some(route_path) = route.route_raw() else {
2929+ // Skip routes without a base path (variants-only routes)
3030+ continue;
3131+ };
30323131- // Static and dynamic routes share the same interface for building, but static routes do not require any parameters.
3232- // As such, we can just pass an empty set of parameters (the default for PageParams).
3333- let params = PageParams::default();
3333+ // Extract parameters from the route path to determine if it's static or dynamic
3434+ let params = extract_params_from_raw_route(&route_path);
34353535- // Every page has a PageContext, which contains information about the current page, as well as access to content and assets.
3636- let url = route.url(¶ms);
3737- let mut ctx = PageContext::from_static_route(
3838- &content_sources,
3939- &mut page_assets,
4040- &url,
4141- &options.base_url,
4242- None,
4343- );
3636+ if params.is_empty() {
3737+ // Static route - no parameters in the path
44384545- let content = route.build(&mut ctx)?;
3939+ // Our page does not include content or assets, but we'll set those up for future use.
4040+ let mut page_assets = RouteAssets::new(&route_assets_options, None);
46414747- let page_filepath = route.file_path(¶ms, &options.output_dir);
4242+ // Static routes do not require any parameters.
4343+ // As such, we can just pass an empty set of parameters (the default for PageParams).
4444+ let params = PageParams::default();
48454949- // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail.
5050- if let Some(parent_dir) = page_filepath.parent() {
5151- fs::create_dir_all(parent_dir)?
5252- }
4646+ // Every page has a PageContext, which contains information about the current page, as well as access to content and assets.
4747+ let url = route.url(¶ms);
4848+ let mut ctx = PageContext::from_static_route(
4949+ &content_sources,
5050+ &mut page_assets,
5151+ &url,
5252+ &options.base_url,
5353+ None,
5454+ );
53555454- fs::write(page_filepath, content)?;
5656+ let content = route.build(&mut ctx)?;
55575656- // Copy all assets used by this page.
5757- for asset in page_assets.assets() {
5858- fs::copy(asset.path(), asset.build_path())?;
5959- }
5858+ let page_filepath = route.file_path(¶ms, &options.output_dir);
5959+6060+ // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail.
6161+ if let Some(parent_dir) = page_filepath.parent() {
6262+ fs::create_dir_all(parent_dir)?
6063 }
6161- RouteType::Dynamic => {
6262- // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching.
6363- // However, it is not stricly necessary, and you may want to instead create a new instance of RouteAssets especially if you were to parallelize the building of pages.
6464- let mut page_assets = RouteAssets::new(&route_assets_options, None);
65646666- // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties.
6767- // It is very common for dynamic pages to be based on content, for instance a blog post page that has one route per blog post.
6868- // As such, we create essentially a mini `PageContext` through `DynamicRouteContext` that includes the content sources, so that the page can use them to generate its routes.
6969- let mut dynamic_ctx = DynamicRouteContext {
7070- content: &content_sources,
7171- assets: &mut page_assets,
7272- variant: None,
7373- };
6565+ fs::write(page_filepath, content)?;
74667575- let routes = route.get_pages(&mut dynamic_ctx);
6767+ // Copy all assets used by this page.
6868+ for asset in page_assets.assets() {
6969+ fs::copy(asset.path(), asset.build_path())?;
7070+ }
7171+ } else {
7272+ // Dynamic route - has parameters like [slug] or [id]
76737777- for page in routes {
7878- // The dynamic route includes the parameters for this specific page.
7979- let params = &page.0;
7474+ // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching.
7575+ // However, it is not strictly necessary, and you may want to instead create a new instance of RouteAssets especially if you were to parallelize the building of pages.
7676+ let mut page_assets = RouteAssets::new(&route_assets_options, None);
80778181- // Here the context is created from a dynamic route, as the context has to include the page parameters and properties.
8282- let url = route.url(params);
8383- let mut ctx = PageContext {
8484- params: page.1.as_ref(),
8585- props: page.2.as_ref(),
8686- content: &content_sources,
8787- assets: &mut page_assets,
8888- current_path: &url,
8989- base_url: &options.base_url,
9090- variant: None,
9191- };
7878+ // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties.
7979+ // It is very common for dynamic pages to be based on content, for instance a blog post page that has one route per blog post.
8080+ // As such, we create essentially a mini `PageContext` through `DynamicRouteContext` that includes the content sources, so that the page can use them to generate its routes.
8181+ let mut dynamic_ctx = DynamicRouteContext {
8282+ content: &content_sources,
8383+ assets: &mut page_assets,
8484+ variant: None,
8585+ };
92869393- // Everything below here is the same as for static routes.
8787+ let routes = route.get_pages(&mut dynamic_ctx);
94889595- let content = route.build(&mut ctx)?;
8989+ for page in routes {
9090+ // The dynamic route includes the parameters for this specific page.
9191+ let params = &page.0;
96929797- let route_filepath = route.file_path(params, &options.output_dir);
9393+ // Here the context is created from a dynamic route, as the context has to include the page parameters and properties.
9494+ let url = route.url(params);
9595+ let mut ctx = PageContext::from_dynamic_route(
9696+ &page,
9797+ &content_sources,
9898+ &mut page_assets,
9999+ &url,
100100+ &options.base_url,
101101+ None,
102102+ );
981039999- if let Some(parent_dir) = route_filepath.parent() {
100100- fs::create_dir_all(parent_dir)?
101101- }
104104+ // Everything below here is the same as for static routes.
102105103103- fs::write(route_filepath, content)?;
106106+ let content = route.build(&mut ctx)?;
104107105105- for asset in page_assets.assets() {
106106- fs::copy(asset.path(), asset.build_path())?;
107107- }
108108+ let route_filepath = route.file_path(params, &options.output_dir);
109109+110110+ if let Some(parent_dir) = route_filepath.parent() {
111111+ fs::create_dir_all(parent_dir)?
112112+ }
113113+114114+ fs::write(route_filepath, content)?;
115115+116116+ for asset in page_assets.assets() {
117117+ fs::copy(asset.path(), asset.build_path())?;
108118 }
109119 }
110120 }
+46-33
website/content/docs/library.md
···1717```rs
1818use maudit::{
1919 content::ContentSources,
2020- page::{FullRoute, RouteAssets},
2121- routing::{DynamicRouteContext, PageContext, PageParams, RouteType},
2020+ route::{DynamicRouteContext, FullRoute, PageContext, PageParams},
2121+ routing::extract_params_from_raw_route,
2222 BuildOptions,
2323};
24242525pub fn build_website(
2626 routes: &[&dyn FullRoute],
2727 mut content_sources: ContentSources,
2828- options: BuildOptions
2828+ options: &BuildOptions
2929) -> Result<(), Box<dyn std::error::Error>> {
3030 // We'll fill this in later.
3131 Ok(())
···36363737The first step in building our own entrypoint is to iterate over the routes and build each page. Routes can either be static (i.e. `/index`) or dynamic (i.e. `/articles/[id]`). For now, we'll only handle static routes.
38383939+To determine if a route is static or dynamic, we get its raw route path using `route_raw()` and check if it contains any parameters using `extract_params_from_raw_route()`.
4040+3941```rs
4042pub fn build_website(
4143 routes: &[&dyn FullRoute],
4244 mut content_sources: ContentSources,
4343- options: BuildOptions,
4545+ options: &BuildOptions,
4446) -> Result<(), Box<dyn std::error::Error>> {
45474648 // Options we'll be passing to RouteAssets instances.
···4850 let route_assets_options = options.route_assets_options();
49515052 for route in routes {
5151- match route.route_type() {
5252- RouteType::Static => {
5353- // Our page does not include content or assets, but we'll set those up for future use.
5454- // RouteAssets also can take a cache parameter, but we'll leave it empty for simplicity.
5555- let mut route_assets = RouteAssets::new(&route_assets_options, None);
5353+ // Get the raw route path (e.g., "/articles/[slug]")
5454+ let Some(route_path) = route.route_raw() else {
5555+ // Skip routes without a base path (variants-only routes)
5656+ continue;
5757+ };
56585757- // Static and dynamic routes share the same interface for building, but static routes do not require any parameters.
5858- // As such, we can just pass an empty set of parameters (the default for PageParams).
5959- let params = PageParams::default();
5959+ // Extract parameters from the route path to determine if it's static or dynamic
6060+ let params = extract_params_from_raw_route(&route_path);
60616161- // Every page has a PageContext, which contains information about the current route, as well as access to content and assets.
6262- let url = route.url(¶ms);
6363- let mut ctx = PageContext::from_static_route(&content_sources, &mut route_assets, &url, &options.base_url);
6262+ if params.is_empty() {
6363+ // Static route - no parameters in the path
64646565- let content = route.build(&mut ctx)?;
6565+ // Our page does not include content or assets, but we'll set those up for future use.
6666+ // RouteAssets can take an optional image cache parameter, but we'll leave it as None for simplicity.
6767+ let mut route_assets = RouteAssets::new(&route_assets_options, None);
6868+6969+ // Static and dynamic routes share the same interface for building, but static routes do not require any parameters.
7070+ // As such, we can just pass an empty set of parameters (the default for PageParams).
7171+ let params = PageParams::default();
7272+7373+ // Every page has a PageContext, which contains information about the current route, as well as access to content and assets.
7474+ let url = route.url(¶ms);
7575+ let mut ctx = PageContext::from_static_route(&content_sources, &mut route_assets, &url, &options.base_url, None);
66766767- let route_filepath = route.file_path(¶ms, &options.output_dir);
7777+ let content = route.build(&mut ctx)?;
68786969- // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail.
7070- if let Some(parent_dir) = route_filepath.parent() {
7171- fs::create_dir_all(parent_dir)?
7272- }
7979+ let route_filepath = route.file_path(¶ms, &options.output_dir);
73807474- fs::write(route_filepath, content)?;
8181+ // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail.
8282+ if let Some(parent_dir) = route_filepath.parent() {
8383+ fs::create_dir_all(parent_dir)?
7584 }
7676- RouteType::Dynamic => {
7777- unimplemented!("We'll handle dynamic routes later");
7878- }
8585+8686+ fs::write(route_filepath, content)?;
8787+ } else {
8888+ // We'll handle dynamic routes later
8989+ unimplemented!("Dynamic routes not yet implemented");
7990 }
8091 }
8192···124135```rs
125136// No changes before this block.
126137127127-RouteType::Dynamic => {
138138+} else {
128139 // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching.
129129- // However, it is not stricly necessary, and you may want to instead create a new instance of RouteAssets especially if you were to parallelize the building of pages.
140140+ // However, it is not strictly necessary, and you may want to instead create a new instance of RouteAssets especially if you were to parallelize the building of pages.
130141 let mut page_assets = RouteAssets::new(&route_assets_options, None);
131142132143 // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties.
···135146 let mut dynamic_ctx = DynamicRouteContext {
136147 content: &content_sources,
137148 assets: &mut page_assets,
149149+ variant: None,
138150 };
139151140140- let routes = route.get_pages(&dynamic_ctx);
152152+ let pages = route.get_pages(&dynamic_ctx);
141153142142- for dynamic_route in routes {
154154+ for page in pages {
143155 // The dynamic route includes the parameters for this specific route.
144144- let params = &dynamic_route.0;
156156+ let params = &page.0;
145157146158 // Here the context is created from a dynamic route, as the context has to include the route parameters and properties.
147159 let url = route.url(params);
148160 let mut ctx = PageContext::from_dynamic_route(
149149- &dynamic_route,
161161+ &page,
150162 &content_sources,
151163 &mut page_assets,
152164 &url,
153153- &options.base_url
165165+ &options.base_url,
166166+ None,
154167 );
155168156169 // Everything after this is the same as for static routes.
···160173161174## Conclusion
162175163163-And with that, you've succesfully rebuilt Maudit at home! There's a few more things that can be done to improve this implementation, like adding logging, copying static assets (from [`options.static_dir`](https://docs.rs/maudit/latest/maudit/struct.BuildOptions.html#structfield.static_dir)), asset processing, better error handling, parallelization, caching, etc, etc.
176176+And with that, you've successfully rebuilt Maudit at home! There's a few more things that can be done to improve this implementation, like adding logging, adding support [for variants](/docs/routing/#internationalization-i18n), copying static assets (from [`options.static_dir`](https://docs.rs/maudit/latest/maudit/struct.BuildOptions.html#structfield.static_dir)), asset processing, better error handling, parallelization, caching, etc, etc.
164177165178But, this is a fully functional implementation that can be used as a starting point for more advanced use cases... or just as a learning exercise to understand how Maudit works under the hood.