Rust library to generate static websites
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: update library example

+124 -102
+1 -2
crates/maudit/src/lib.rs
··· 10 10 pub mod content; 11 11 pub mod errors; 12 12 pub mod route; 13 - 14 - mod routing; 13 + pub mod routing; 15 14 16 15 // Exports for end-users 17 16 pub use build::metadata::{BuildOutput, PageOutput, StaticAssetOutput};
+77 -67
examples/library/src/build.rs
··· 4 4 BuildOptions, 5 5 assets::RouteAssets, 6 6 content::ContentSources, 7 - route::{DynamicRouteContext, FullRoute, PageContext, PageParams, RouteType}, 7 + route::{DynamicRouteContext, FullRoute, PageContext, PageParams}, 8 + routing::extract_params_from_raw_route, 8 9 }; 9 10 10 11 pub fn build_website( ··· 12 13 mut content_sources: ContentSources, 13 14 options: &BuildOptions, 14 15 ) -> Result<(), Box<dyn std::error::Error>> { 15 - // Initialize all the content sources; 16 + // Initialize all the content sources 16 17 content_sources.init_all(); 17 18 18 19 // Options we'll be passing to RouteAssets instances. ··· 20 21 let route_assets_options = options.route_assets_options(); 21 22 22 23 // Create the assets directory if it doesn't exist. 23 - fs::create_dir_all(&route_assets_options.assets_dir)?; 24 + fs::create_dir_all(&route_assets_options.output_assets_dir)?; 24 25 25 26 for route in routes { 26 - match route.route_type() { 27 - RouteType::Static => { 28 - // Our page does not include content or assets, but we'll set those up for future use. 29 - let mut page_assets = RouteAssets::new(&route_assets_options, None); 27 + // Get the raw route path (e.g., "/articles/[slug]") 28 + let Some(route_path) = route.route_raw() else { 29 + // Skip routes without a base path (variants-only routes) 30 + continue; 31 + }; 30 32 31 - // Static and dynamic routes share the same interface for building, but static routes do not require any parameters. 32 - // As such, we can just pass an empty set of parameters (the default for PageParams). 33 - let params = PageParams::default(); 33 + // Extract parameters from the route path to determine if it's static or dynamic 34 + let params = extract_params_from_raw_route(&route_path); 34 35 35 - // Every page has a PageContext, which contains information about the current page, as well as access to content and assets. 36 - let url = route.url(&params); 37 - let mut ctx = PageContext::from_static_route( 38 - &content_sources, 39 - &mut page_assets, 40 - &url, 41 - &options.base_url, 42 - None, 43 - ); 36 + if params.is_empty() { 37 + // Static route - no parameters in the path 44 38 45 - let content = route.build(&mut ctx)?; 39 + // Our page does not include content or assets, but we'll set those up for future use. 40 + let mut page_assets = RouteAssets::new(&route_assets_options, None); 46 41 47 - let page_filepath = route.file_path(&params, &options.output_dir); 42 + // Static routes do not require any parameters. 43 + // As such, we can just pass an empty set of parameters (the default for PageParams). 44 + let params = PageParams::default(); 48 45 49 - // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail. 50 - if let Some(parent_dir) = page_filepath.parent() { 51 - fs::create_dir_all(parent_dir)? 52 - } 46 + // Every page has a PageContext, which contains information about the current page, as well as access to content and assets. 47 + let url = route.url(&params); 48 + let mut ctx = PageContext::from_static_route( 49 + &content_sources, 50 + &mut page_assets, 51 + &url, 52 + &options.base_url, 53 + None, 54 + ); 53 55 54 - fs::write(page_filepath, content)?; 56 + let content = route.build(&mut ctx)?; 55 57 56 - // Copy all assets used by this page. 57 - for asset in page_assets.assets() { 58 - fs::copy(asset.path(), asset.build_path())?; 59 - } 58 + let page_filepath = route.file_path(&params, &options.output_dir); 59 + 60 + // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail. 61 + if let Some(parent_dir) = page_filepath.parent() { 62 + fs::create_dir_all(parent_dir)? 60 63 } 61 - RouteType::Dynamic => { 62 - // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching. 63 - // 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. 64 - let mut page_assets = RouteAssets::new(&route_assets_options, None); 65 64 66 - // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties. 67 - // 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. 68 - // 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. 69 - let mut dynamic_ctx = DynamicRouteContext { 70 - content: &content_sources, 71 - assets: &mut page_assets, 72 - variant: None, 73 - }; 65 + fs::write(page_filepath, content)?; 74 66 75 - let routes = route.get_pages(&mut dynamic_ctx); 67 + // Copy all assets used by this page. 68 + for asset in page_assets.assets() { 69 + fs::copy(asset.path(), asset.build_path())?; 70 + } 71 + } else { 72 + // Dynamic route - has parameters like [slug] or [id] 76 73 77 - for page in routes { 78 - // The dynamic route includes the parameters for this specific page. 79 - let params = &page.0; 74 + // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching. 75 + // 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. 76 + let mut page_assets = RouteAssets::new(&route_assets_options, None); 80 77 81 - // Here the context is created from a dynamic route, as the context has to include the page parameters and properties. 82 - let url = route.url(params); 83 - let mut ctx = PageContext { 84 - params: page.1.as_ref(), 85 - props: page.2.as_ref(), 86 - content: &content_sources, 87 - assets: &mut page_assets, 88 - current_path: &url, 89 - base_url: &options.base_url, 90 - variant: None, 91 - }; 78 + // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties. 79 + // 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. 80 + // 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. 81 + let mut dynamic_ctx = DynamicRouteContext { 82 + content: &content_sources, 83 + assets: &mut page_assets, 84 + variant: None, 85 + }; 92 86 93 - // Everything below here is the same as for static routes. 87 + let routes = route.get_pages(&mut dynamic_ctx); 94 88 95 - let content = route.build(&mut ctx)?; 89 + for page in routes { 90 + // The dynamic route includes the parameters for this specific page. 91 + let params = &page.0; 96 92 97 - let route_filepath = route.file_path(params, &options.output_dir); 93 + // Here the context is created from a dynamic route, as the context has to include the page parameters and properties. 94 + let url = route.url(params); 95 + let mut ctx = PageContext::from_dynamic_route( 96 + &page, 97 + &content_sources, 98 + &mut page_assets, 99 + &url, 100 + &options.base_url, 101 + None, 102 + ); 98 103 99 - if let Some(parent_dir) = route_filepath.parent() { 100 - fs::create_dir_all(parent_dir)? 101 - } 104 + // Everything below here is the same as for static routes. 102 105 103 - fs::write(route_filepath, content)?; 106 + let content = route.build(&mut ctx)?; 104 107 105 - for asset in page_assets.assets() { 106 - fs::copy(asset.path(), asset.build_path())?; 107 - } 108 + let route_filepath = route.file_path(params, &options.output_dir); 109 + 110 + if let Some(parent_dir) = route_filepath.parent() { 111 + fs::create_dir_all(parent_dir)? 112 + } 113 + 114 + fs::write(route_filepath, content)?; 115 + 116 + for asset in page_assets.assets() { 117 + fs::copy(asset.path(), asset.build_path())?; 108 118 } 109 119 } 110 120 }
+46 -33
website/content/docs/library.md
··· 17 17 ```rs 18 18 use maudit::{ 19 19 content::ContentSources, 20 - page::{FullRoute, RouteAssets}, 21 - routing::{DynamicRouteContext, PageContext, PageParams, RouteType}, 20 + route::{DynamicRouteContext, FullRoute, PageContext, PageParams}, 21 + routing::extract_params_from_raw_route, 22 22 BuildOptions, 23 23 }; 24 24 25 25 pub fn build_website( 26 26 routes: &[&dyn FullRoute], 27 27 mut content_sources: ContentSources, 28 - options: BuildOptions 28 + options: &BuildOptions 29 29 ) -> Result<(), Box<dyn std::error::Error>> { 30 30 // We'll fill this in later. 31 31 Ok(()) ··· 36 36 37 37 The 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. 38 38 39 + 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()`. 40 + 39 41 ```rs 40 42 pub fn build_website( 41 43 routes: &[&dyn FullRoute], 42 44 mut content_sources: ContentSources, 43 - options: BuildOptions, 45 + options: &BuildOptions, 44 46 ) -> Result<(), Box<dyn std::error::Error>> { 45 47 46 48 // Options we'll be passing to RouteAssets instances. ··· 48 50 let route_assets_options = options.route_assets_options(); 49 51 50 52 for route in routes { 51 - match route.route_type() { 52 - RouteType::Static => { 53 - // Our page does not include content or assets, but we'll set those up for future use. 54 - // RouteAssets also can take a cache parameter, but we'll leave it empty for simplicity. 55 - let mut route_assets = RouteAssets::new(&route_assets_options, None); 53 + // Get the raw route path (e.g., "/articles/[slug]") 54 + let Some(route_path) = route.route_raw() else { 55 + // Skip routes without a base path (variants-only routes) 56 + continue; 57 + }; 56 58 57 - // Static and dynamic routes share the same interface for building, but static routes do not require any parameters. 58 - // As such, we can just pass an empty set of parameters (the default for PageParams). 59 - let params = PageParams::default(); 59 + // Extract parameters from the route path to determine if it's static or dynamic 60 + let params = extract_params_from_raw_route(&route_path); 60 61 61 - // Every page has a PageContext, which contains information about the current route, as well as access to content and assets. 62 - let url = route.url(&params); 63 - let mut ctx = PageContext::from_static_route(&content_sources, &mut route_assets, &url, &options.base_url); 62 + if params.is_empty() { 63 + // Static route - no parameters in the path 64 64 65 - let content = route.build(&mut ctx)?; 65 + // Our page does not include content or assets, but we'll set those up for future use. 66 + // RouteAssets can take an optional image cache parameter, but we'll leave it as None for simplicity. 67 + let mut route_assets = RouteAssets::new(&route_assets_options, None); 68 + 69 + // Static and dynamic routes share the same interface for building, but static routes do not require any parameters. 70 + // As such, we can just pass an empty set of parameters (the default for PageParams). 71 + let params = PageParams::default(); 72 + 73 + // Every page has a PageContext, which contains information about the current route, as well as access to content and assets. 74 + let url = route.url(&params); 75 + let mut ctx = PageContext::from_static_route(&content_sources, &mut route_assets, &url, &options.base_url, None); 66 76 67 - let route_filepath = route.file_path(&params, &options.output_dir); 77 + let content = route.build(&mut ctx)?; 68 78 69 - // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail. 70 - if let Some(parent_dir) = route_filepath.parent() { 71 - fs::create_dir_all(parent_dir)? 72 - } 79 + let route_filepath = route.file_path(&params, &options.output_dir); 73 80 74 - fs::write(route_filepath, content)?; 81 + // On some platforms, creating a file in a nested directory requires that the directory already exists or the file creation will fail. 82 + if let Some(parent_dir) = route_filepath.parent() { 83 + fs::create_dir_all(parent_dir)? 75 84 } 76 - RouteType::Dynamic => { 77 - unimplemented!("We'll handle dynamic routes later"); 78 - } 85 + 86 + fs::write(route_filepath, content)?; 87 + } else { 88 + // We'll handle dynamic routes later 89 + unimplemented!("Dynamic routes not yet implemented"); 79 90 } 80 91 } 81 92 ··· 124 135 ```rs 125 136 // No changes before this block. 126 137 127 - RouteType::Dynamic => { 138 + } else { 128 139 // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching. 129 - // 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. 140 + // 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. 130 141 let mut page_assets = RouteAssets::new(&route_assets_options, None); 131 142 132 143 // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties. ··· 135 146 let mut dynamic_ctx = DynamicRouteContext { 136 147 content: &content_sources, 137 148 assets: &mut page_assets, 149 + variant: None, 138 150 }; 139 151 140 - let routes = route.get_pages(&dynamic_ctx); 152 + let pages = route.get_pages(&dynamic_ctx); 141 153 142 - for dynamic_route in routes { 154 + for page in pages { 143 155 // The dynamic route includes the parameters for this specific route. 144 - let params = &dynamic_route.0; 156 + let params = &page.0; 145 157 146 158 // Here the context is created from a dynamic route, as the context has to include the route parameters and properties. 147 159 let url = route.url(params); 148 160 let mut ctx = PageContext::from_dynamic_route( 149 - &dynamic_route, 161 + &page, 150 162 &content_sources, 151 163 &mut page_assets, 152 164 &url, 153 - &options.base_url 165 + &options.base_url, 166 + None, 154 167 ); 155 168 156 169 // Everything after this is the same as for static routes. ··· 160 173 161 174 ## Conclusion 162 175 163 - 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. 176 + 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. 164 177 165 178 But, 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.