Rust library to generate static websites
5
fork

Configure Feed

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

refactor: pass contentsources directly to pages instead of using a wrapper (#64)

* refactor: pass contentsources directly to pages instead of using a wrapper

* fix: update docs on library

authored by

Erika and committed by
GitHub
ee7e1281 7c431ae9

+109 -172
+4 -6
crates/maudit/src/build.rs
··· 15 15 image_cache::{IMAGE_CACHE_DIR, ImageCache}, 16 16 }, 17 17 build::images::process_image, 18 - content::{ContentSources, RouteContent}, 18 + content::ContentSources, 19 19 is_dev, 20 20 logging::print_title, 21 21 route::{ ··· 130 130 RouteType::Static => { 131 131 let route_start = Instant::now(); 132 132 133 - let content = RouteContent::new(content_sources); 134 133 let mut page_assets = RouteAssets::new(&route_assets_options); 135 134 136 135 let params = PageParams::default(); 137 136 let url = cached_route.url(&params); 138 137 139 138 let result = route.build(&mut PageContext::from_static_route( 140 - &content, 139 + content_sources, 141 140 &mut page_assets, 142 141 &url, 143 142 &options.base_url, ··· 162 161 page_count += 1; 163 162 } 164 163 RouteType::Dynamic => { 165 - let content = RouteContent::new(content_sources); 166 164 let mut page_assets = RouteAssets::new(&route_assets_options); 167 165 168 166 let pages = route.get_pages(&mut DynamicRouteContext { 169 - content: &content, 167 + content: content_sources, 170 168 assets: &mut page_assets, 171 169 }); 172 170 ··· 184 182 185 183 let content = route.build(&mut PageContext::from_dynamic_route( 186 184 &page, 187 - &content, 185 + content_sources, 188 186 &mut page_assets, 189 187 &url, 190 188 &options.base_url,
+80 -127
crates/maudit/src/content.rs
··· 84 84 /// ``` 85 85 pub use maudit_macros::markdown_entry; 86 86 87 - /// Main struct to access all content sources. 88 - /// 89 - /// Can only access content sources that have been defined in [`coronate()`](crate::coronate). 90 - /// 91 - /// # Example 92 - /// In `main.rs`: 93 - /// ```rust 94 - /// use maudit::{coronate, content_sources, routes, BuildOptions, BuildOutput}; 95 - /// use maudit::content::{markdown_entry, glob_markdown}; 96 - /// 97 - /// #[markdown_entry] 98 - /// pub struct ArticleContent { 99 - /// pub title: String, 100 - /// pub description: String, 101 - /// } 102 - /// 103 - /// fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> { 104 - /// coronate( 105 - /// routes![], 106 - /// content_sources![ 107 - /// "articles" => glob_markdown::<ArticleContent>("content/articles/*.md") 108 - /// ], 109 - /// BuildOptions::default(), 110 - /// ) 111 - /// } 112 - /// ``` 113 - /// 114 - /// In a page: 115 - /// ```rust 116 - /// use maudit::route::prelude::*; 117 - /// # use maudit::content::markdown_entry; 118 - /// # 119 - /// # #[markdown_entry] 120 - /// # pub struct ArticleContent { 121 - /// # pub title: String, 122 - /// # pub description: String, 123 - /// # } 124 - /// 125 - /// #[route("/articles/[article]")] 126 - /// pub struct Article; 127 - /// 128 - /// #[derive(Params, Clone)] 129 - /// pub struct ArticleParams { 130 - /// pub article: String, 131 - /// } 132 - /// 133 - /// impl Route<ArticleParams> for Article { 134 - /// fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 135 - /// let params = ctx.params::<ArticleParams>(); 136 - /// let articles = ctx.content.get_source::<ArticleContent>("articles"); 137 - /// let article = articles.get_entry(&params.article); 138 - /// article.render(ctx) 139 - /// } 140 - /// 141 - /// fn pages(&self, ctx: &mut DynamicRouteContext) -> Pages<ArticleParams> { 142 - /// let articles = ctx.content.get_source::<ArticleContent>("articles"); 143 - /// 144 - /// articles.into_pages(|entry| Page::from_params(ArticleParams { 145 - /// article: entry.id.clone(), 146 - /// })) 147 - /// } 148 - /// } 149 - /// ``` 150 - pub struct RouteContent<'a> { 151 - sources: &'a [Box<dyn ContentSourceInternal>], 152 - } 153 - 154 - impl RouteContent<'_> { 155 - pub fn new(sources: &'_ ContentSources) -> RouteContent<'_> { 156 - RouteContent { 157 - sources: sources.sources(), 158 - } 159 - } 160 - 161 - pub fn get_untyped_source(&self, name: &str) -> &ContentSource<Untyped> { 162 - self.sources 163 - .iter() 164 - .find_map( 165 - |source| match source.as_any().downcast_ref::<ContentSource<Untyped>>() { 166 - Some(source) if source.name == name => Some(source), 167 - _ => None, 168 - }, 169 - ) 170 - .unwrap_or_else(|| panic!("Content source with name '{}' not found", name)) 171 - } 172 - 173 - pub fn get_untyped_source_safe(&self, name: &str) -> Option<&ContentSource<Untyped>> { 174 - self.sources.iter().find_map(|source| { 175 - match source.as_any().downcast_ref::<ContentSource<Untyped>>() { 176 - Some(source) if source.name == name => Some(source), 177 - _ => None, 178 - } 179 - }) 180 - } 181 - 182 - pub fn get_source<T: 'static>(&self, name: &str) -> &ContentSource<T> { 183 - self.sources 184 - .iter() 185 - .find_map( 186 - |source| match source.as_any().downcast_ref::<ContentSource<T>>() { 187 - Some(source) if source.name == name => Some(source), 188 - _ => None, 189 - }, 190 - ) 191 - .unwrap_or_else(|| panic!("Content source with name '{}' not found", name)) 192 - } 193 - 194 - pub fn get_source_safe<T: 'static>(&self, name: &str) -> Option<&ContentSource<T>> { 195 - self.sources.iter().find_map(|source| { 196 - match source.as_any().downcast_ref::<ContentSource<T>>() { 197 - Some(source) if source.name == name => Some(source), 198 - _ => None, 199 - } 200 - }) 201 - } 202 - } 203 - 204 87 /// A single entry of a [`ContentSource`]. 205 88 /// 206 89 /// ## Example ··· 283 166 284 167 /// Trait for contexts that can provide access to content 285 168 pub trait ContentContext { 286 - fn content(&self) -> &RouteContent<'_>; 169 + fn content(&self) -> &ContentSources; 287 170 fn assets(&mut self) -> &mut RouteAssets; 288 171 } 289 172 290 173 impl ContentContext for PageContext<'_> { 291 - fn content(&self) -> &RouteContent<'_> { 174 + fn content(&self) -> &ContentSources { 292 175 self.content 293 176 } 294 177 ··· 298 181 } 299 182 300 183 impl ContentContext for DynamicRouteContext<'_> { 301 - fn content(&self) -> &RouteContent<'_> { 184 + fn content(&self) -> &ContentSources { 302 185 self.content 303 186 } 304 187 ··· 331 214 /// Represents an untyped content source. 332 215 pub type Untyped = FxHashMap<String, String>; 333 216 334 - /// Represents a collection of content sources. 217 + /// Main struct to access all content sources. 335 218 /// 336 - /// Mostly seen as the return type of [`content_sources!`](crate::content_sources). 219 + /// # Example 220 + /// In `main.rs`: 221 + /// ```rust 222 + /// use maudit::{coronate, content_sources, routes, BuildOptions, BuildOutput}; 223 + /// use maudit::content::{markdown_entry, glob_markdown}; 224 + /// 225 + /// #[markdown_entry] 226 + /// pub struct ArticleContent { 227 + /// pub title: String, 228 + /// pub description: String, 229 + /// } 337 230 /// 338 - /// ## Example 231 + /// fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> { 232 + /// coronate( 233 + /// routes![], 234 + /// content_sources![ 235 + /// "articles" => glob_markdown::<ArticleContent>("content/articles/*.md") 236 + /// ], 237 + /// BuildOptions::default(), 238 + /// ) 239 + /// } 240 + /// ``` 241 + /// 242 + /// In a page: 339 243 /// ```rust 340 244 /// use maudit::route::prelude::*; 341 - /// use maudit::content::{glob_markdown, ContentSources}; 342 - /// use maudit::content_sources; 343 245 /// # use maudit::content::markdown_entry; 344 246 /// # 345 247 /// # #[markdown_entry] ··· 348 250 /// # pub description: String, 349 251 /// # } 350 252 /// 351 - /// pub fn content_sources() -> ContentSources { 352 - /// content_sources!["docs" => glob_markdown::<ArticleContent>("content/docs/*.md")] 253 + /// #[route("/articles/[article]")] 254 + /// pub struct Article; 255 + /// 256 + /// #[derive(Params, Clone)] 257 + /// pub struct ArticleParams { 258 + /// pub article: String, 353 259 /// } 260 + /// 261 + /// impl Route<ArticleParams> for Article { 262 + /// fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 263 + /// let params = ctx.params::<ArticleParams>(); 264 + /// let articles = ctx.content.get_source::<ArticleContent>("articles"); 265 + /// let article = articles.get_entry(&params.article); 266 + /// article.render(ctx) 267 + /// } 268 + /// 269 + /// fn pages(&self, ctx: &mut DynamicRouteContext) -> Pages<ArticleParams> { 270 + /// let articles = ctx.content.get_source::<ArticleContent>("articles"); 271 + /// 272 + /// articles.into_pages(|entry| Page::from_params(ArticleParams { 273 + /// article: entry.id.clone(), 274 + /// })) 275 + /// } 276 + /// } 277 + /// ``` 354 278 pub struct ContentSources(pub Vec<Box<dyn ContentSourceInternal>>); 355 279 356 280 impl From<Vec<Box<dyn ContentSourceInternal>>> for ContentSources { ··· 376 300 for source in &mut self.0 { 377 301 source.init(); 378 302 } 303 + } 304 + 305 + pub fn get_untyped_source(&self, name: &str) -> &ContentSource<Untyped> { 306 + self.get_source::<Untyped>(name) 307 + } 308 + 309 + pub fn get_untyped_source_safe(&self, name: &str) -> Option<&ContentSource<Untyped>> { 310 + self.get_source_safe::<Untyped>(name) 311 + } 312 + 313 + pub fn get_source<T: 'static>(&self, name: &str) -> &ContentSource<T> { 314 + self.0 315 + .iter() 316 + .find_map( 317 + |source| match source.as_any().downcast_ref::<ContentSource<T>>() { 318 + Some(source) if source.name == name => Some(source), 319 + _ => None, 320 + }, 321 + ) 322 + .unwrap_or_else(|| panic!("Content source with name '{}' not found", name)) 323 + } 324 + 325 + pub fn get_source_safe<T: 'static>(&self, name: &str) -> Option<&ContentSource<T>> { 326 + self.0.iter().find_map( 327 + |source| match source.as_any().downcast_ref::<ContentSource<T>>() { 328 + Some(source) if source.name == name => Some(source), 329 + _ => None, 330 + }, 331 + ) 379 332 } 380 333 } 381 334
+2 -6
crates/maudit/src/content/markdown/shortcodes_tests.rs
··· 57 57 where 58 58 F: for<'a> FnOnce(&mut PageContext<'a>) -> R, 59 59 { 60 - use crate::{ 61 - assets::RouteAssets, 62 - content::{ContentSources, RouteContent}, 63 - }; 60 + use crate::{assets::RouteAssets, content::ContentSources}; 64 61 65 62 let content_sources = ContentSources::new(vec![]); 66 - let content = RouteContent::new(&content_sources); 67 63 let mut page_assets = RouteAssets::new(&RouteAssetsOptions { 68 64 assets_dir: "assets".into(), 69 65 ..Default::default() 70 66 }); 71 67 72 68 let mut ctx = PageContext { 73 - content: &content, 69 + content: &content_sources, 74 70 assets: &mut page_assets, 75 71 current_path: &"/test".to_string(), 76 72 params: &(),
+6 -8
crates/maudit/src/route.rs
··· 2 2 //! 3 3 //! Every route must implement the [`Route`] trait. Then, pages can be passed to [`coronate()`](crate::coronate), through the [`routes!`](crate::routes) macro, to be built. 4 4 use crate::assets::{Asset, RouteAssets}; 5 - use crate::content::{Entry, RouteContent}; 5 + use crate::content::{ContentSources, Entry}; 6 6 use crate::errors::BuildError; 7 7 use crate::routing::{ 8 8 extract_params_from_raw_route, get_route_type_from_route_params, guess_if_route_is_endpoint, ··· 269 269 pub struct PageContext<'a> { 270 270 pub params: &'a dyn Any, 271 271 pub props: &'a dyn Any, 272 - pub content: &'a RouteContent<'a>, 272 + pub content: &'a ContentSources, 273 273 pub assets: &'a mut RouteAssets, 274 274 /// The current path being rendered, e.g. `/articles/my-article`. 275 275 pub current_path: &'a String, ··· 279 279 280 280 impl<'a> PageContext<'a> { 281 281 pub fn from_static_route( 282 - content: &'a RouteContent, 282 + content: &'a ContentSources, 283 283 assets: &'a mut RouteAssets, 284 284 current_path: &'a String, 285 285 base_url: &'a Option<String>, ··· 296 296 297 297 pub fn from_dynamic_route( 298 298 dynamic_page: &'a PagesResult, 299 - content: &'a RouteContent, 299 + content: &'a ContentSources, 300 300 assets: &'a mut RouteAssets, 301 301 current_path: &'a String, 302 302 base_url: &'a Option<String>, ··· 384 384 /// } 385 385 /// ``` 386 386 pub struct DynamicRouteContext<'a> { 387 - pub content: &'a RouteContent<'a>, 387 + pub content: &'a ContentSources, 388 388 pub assets: &'a mut RouteAssets, 389 389 } 390 390 ··· 741 741 Asset, Image, ImageFormat, ImageOptions, ImagePlaceholder, RenderWithAlt, Script, Style, 742 742 StyleOptions, 743 743 }; 744 - pub use crate::content::{ 745 - ContentContext, ContentEntry, Entry, EntryInner, MarkdownContent, RouteContent, 746 - }; 744 + pub use crate::content::{ContentContext, ContentEntry, Entry, EntryInner, MarkdownContent}; 747 745 pub use maudit_macros::{Params, route}; 748 746 } 749 747
+8 -13
examples/library/src/build.rs
··· 3 3 use maudit::{ 4 4 BuildOptions, 5 5 assets::RouteAssets, 6 - content::{ContentSources, RouteContent}, 6 + content::ContentSources, 7 7 route::{DynamicRouteContext, FullRoute, PageContext, PageParams, RouteType}, 8 8 }; 9 9 ··· 26 26 match route.route_type() { 27 27 RouteType::Static => { 28 28 // Our page does not include content or assets, but we'll set those up for future use. 29 - let content = RouteContent::new(&content_sources); 30 29 let mut page_assets = RouteAssets::new(&route_assets_options); 31 30 32 31 // Static and dynamic routes share the same interface for building, but static routes do not require any parameters. ··· 36 35 // Every page has a PageContext, which contains information about the current page, as well as access to content and assets. 37 36 let url = route.url(&params); 38 37 let mut ctx = PageContext::from_static_route( 39 - &content, 38 + &content_sources, 40 39 &mut page_assets, 41 40 &url, 42 41 &options.base_url, ··· 59 58 } 60 59 } 61 60 RouteType::Dynamic => { 62 - // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties. 63 - // 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. 64 - // As such, we create a mini PageContext that includes the content sources, so that the route can use them to generate its pages. 65 - 66 - // Every page of a route may share a reference to the same RouteContent and RouteAssets instance, as it can help with caching. 61 + // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching. 67 62 // 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. 68 - let content = RouteContent::new(&content_sources); 69 63 let mut page_assets = RouteAssets::new(&route_assets_options); 70 64 65 + // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties. 66 + // 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. 67 + // 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. 71 68 let mut dynamic_ctx = DynamicRouteContext { 72 - content: &content, 69 + content: &content_sources, 73 70 assets: &mut page_assets, 74 71 }; 75 72 76 73 let routes = route.get_pages(&mut dynamic_ctx); 77 - 78 - let content = RouteContent::new(&content_sources); 79 74 80 75 for page in routes { 81 76 // The dynamic route includes the parameters for this specific page. ··· 85 80 let url = route.url(params); 86 81 let mut ctx = PageContext::from_dynamic_route( 87 82 &page, 88 - &content, 83 + &content_sources, 89 84 &mut page_assets, 90 85 &url, 91 86 &options.base_url,
+9 -12
website/content/docs/library.md
··· 17 17 ```rs 18 18 use maudit::{ 19 19 content::ContentSources, 20 - page::{FullRoute, RouteAssets, RouteContent}, 20 + page::{FullRoute, RouteAssets}, 21 21 routing::{DynamicRouteContext, PageContext, PageParams, RouteType}, 22 22 BuildOptions, 23 23 }; ··· 51 51 match route.route_type() { 52 52 RouteType::Static => { 53 53 // Our page does not include content or assets, but we'll set those up for future use. 54 - let content = RouteContent::new(&content_sources); 55 54 let mut route_assets = RouteAssets::new(&route_assets_options); 56 55 57 56 // Static and dynamic routes share the same interface for building, but static routes do not require any parameters. ··· 60 59 61 60 // Every page has a PageContext, which contains information about the current route, as well as access to content and assets. 62 61 let url = route.url(&params); 63 - let mut ctx = PageContext::from_static_route(&content, &mut route_assets, &url, &options.base_url); 62 + let mut ctx = PageContext::from_static_route(&content_sources, &mut route_assets, &url, &options.base_url); 64 63 65 64 let content = route.build(&mut ctx)?; 66 65 ··· 125 124 // No changes before this block. 126 125 127 126 RouteType::Dynamic => { 127 + // Every page of a dynamic route may share a reference to the same RouteAssets instance, as it can help with caching. 128 + // 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. 129 + let mut page_assets = RouteAssets::new(&route_assets_options); 130 + 128 131 // The `get_pages` method returns all the possible pages for this route, along with their parameters and properties. 129 132 // 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. 130 133 // 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. 131 - 132 - // Every page of a route may share a reference to the same RouteContent and RouteAssets instance, as it can help with caching. 133 - // 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. 134 - let mut page_assets = RouteAssets::new(&route_assets_options); 135 - let content = RouteContent::new(&content_sources); 136 - 137 - let dynamic_ctx = DynamicRouteContext { 138 - content: &content, 134 + let mut dynamic_ctx = DynamicRouteContext { 135 + content: &content_sources, 139 136 assets: &mut page_assets, 140 137 }; 141 138 ··· 149 146 let url = route.url(params); 150 147 let mut ctx = PageContext::from_dynamic_route( 151 148 &dynamic_route, 152 - &content, 149 + &content_sources, 153 150 &mut page_assets, 154 151 &url, 155 152 &options.base_url