Rust library to generate static websites
5
fork

Configure Feed

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

fix: some protgress

+132 -40
+6 -7
crates/maudit-macros/src/lib.rs
··· 65 65 66 66 // First argument: either a path expression or a named argument like locales(...) 67 67 if input.peek(Ident) && input.peek2(syn::token::Paren) { 68 - // First argument is a named argument (e.g., locales(...)) 69 - // This means it's a variant-only route with no base path 68 + // If the first argument is a named one, that means there's no base path and this route should only have variants 70 69 let ident: Ident = input.parse()?; 71 70 let ident_str = ident.to_string(); 72 71 ··· 82 81 )); 83 82 } 84 83 } else { 85 - // First argument is a path expression 84 + // First argument is a path expression, e.g., "/about" so proceed as normal 86 85 path = Some(input.parse::<Expr>()?); 87 86 } 88 87 89 - // Parse remaining named arguments (locales, middleware, etc.) 88 + // Parse remaining named arguments (right now just locales(...)) 90 89 while !input.is_empty() { 91 90 input.parse::<Token![,]>()?; 92 91 ··· 94 93 break; 95 94 } 96 95 97 - // All subsequent arguments must be named (e.g., locales(...), middleware(...)) 96 + // All subsequent arguments must be named (e.g., locales(...), the path must be first) 98 97 if input.peek(Ident) && input.peek2(syn::token::Paren) { 99 98 let ident: Ident = input.parse()?; 100 99 let ident_str = ident.to_string(); ··· 160 159 let struct_name = &item_struct.ident; 161 160 162 161 // Generate variants method based on locales 163 - let variant_methods = if !args.locales.is_empty() { 162 + let variant_method = if !args.locales.is_empty() { 164 163 let variant_tuples = args.locales.iter().map(|variant| { 165 164 let locale_name = variant.locale.to_string(); 166 165 ··· 218 217 impl maudit::route::InternalRoute for #struct_name { 219 218 #route_raw_impl 220 219 221 - #variant_methods 220 + #variant_method 222 221 } 223 222 224 223 impl maudit::route::FullRoute for #struct_name {
+10 -25
crates/maudit/src/build.rs
··· 15 15 content::ContentSources, 16 16 is_dev, 17 17 logging::print_title, 18 - route::{ 19 - CachedRoute, DynamicRouteContext, FullRoute, InternalRoute, PageContext, PageParams, 20 - build_file_path_with_params, build_url_with_params, 21 - }, 22 - routing::{extract_params_from_raw_route, guess_if_route_is_endpoint}, 18 + route::{CachedRoute, DynamicRouteContext, FullRoute, InternalRoute, PageContext, PageParams}, 19 + routing::extract_params_from_raw_route, 23 20 }; 24 21 use colored::{ColoredString, Colorize}; 25 22 use log::{debug, info, trace, warn}; ··· 239 236 let mut route_assets = 240 237 RouteAssets::new(&route_assets_options, Some(image_cache.clone())); 241 238 242 - let url = if variant_path.starts_with('/') { 243 - variant_path.clone() 244 - } else { 245 - format!("/{}", variant_path) 246 - }; 239 + let params = PageParams::default(); 240 + let url = cached_route.variant_url(&params, &variant_id)?; 247 241 248 242 let result = route.build(&mut PageContext::from_static_route( 249 243 content_sources, ··· 253 247 Some(variant_id.clone()), 254 248 ))?; 255 249 256 - let file_path = options 257 - .output_dir 258 - .join(variant_path.trim_start_matches('/')) 259 - .join("index.html"); 250 + let file_path = 251 + cached_route.variant_file_path(&params, &options.output_dir, &variant_id)?; 260 252 261 253 write_route_file(&result, &file_path)?; 262 254 ··· 295 287 warn!(target: "build", "Variant {} has dynamic parameters but Route::pages returned an empty Vec.", variant_id.bold()); 296 288 } else { 297 289 for page in pages { 298 - let url = build_url_with_params( 299 - &variant_path, 300 - &variant_params, 301 - &page.0, 302 - guess_if_route_is_endpoint(&variant_path), 303 - ); 290 + let url = cached_route.variant_url(&page.0, &variant_id)?; 304 291 305 292 let content = route.build(&mut PageContext::from_dynamic_route( 306 293 &page, ··· 311 298 Some(variant_id.clone()), 312 299 ))?; 313 300 314 - let file_path = build_file_path_with_params( 315 - &variant_path, 316 - &variant_params, 301 + let file_path = cached_route.variant_file_path( 317 302 &page.0, 318 303 &options.output_dir, 319 - guess_if_route_is_endpoint(&variant_path), 320 - ); 304 + &variant_id, 305 + )?; 321 306 322 307 write_route_file(&content, &file_path)?; 323 308
+116 -8
crates/maudit/src/route.rs
··· 285 285 base_url: &'a Option<String>, 286 286 variant: Option<String>, 287 287 ) -> Self { 288 - PageContext { 288 + Self { 289 289 params: &(), 290 290 props: &(), 291 291 content, ··· 479 479 fn is_endpoint(&self) -> bool { 480 480 guess_if_route_is_endpoint(&self.route_raw()) 481 481 } 482 + 483 + #[deprecated] 482 484 fn route_type(&self) -> RouteType { 483 485 let params_def = extract_params_from_raw_route(&self.route_raw()); 484 486 ··· 500 502 } 501 503 502 504 fn url(&self, params: &PageParams) -> String { 503 - let params_def = extract_params_from_raw_route(&self.route_raw()); 504 - build_url_with_params(&self.route_raw(), &params_def, params, self.is_endpoint()) 505 + let route = self.route_raw(); 506 + let params_def = extract_params_from_raw_route(&route); 507 + build_url_with_params(&route, &params_def, params, self.is_endpoint()) 508 + } 509 + 510 + fn variant_url(&self, params: &PageParams, variant: &str) -> Result<String, String> { 511 + let variants = self.variants(); 512 + let variant_path = variants 513 + .iter() 514 + .find(|(id, _)| id == variant) 515 + .map(|(_, path)| path.clone()) 516 + .ok_or_else(|| format!("Variant '{}' not found", variant))?; 517 + let is_endpoint = guess_if_route_is_endpoint(&variant_path); 518 + let params_def = extract_params_from_raw_route(&variant_path); 519 + Ok(build_url_with_params( 520 + &variant_path, 521 + &params_def, 522 + params, 523 + is_endpoint, 524 + )) 505 525 } 506 526 507 527 fn file_path(&self, params: &PageParams, output_dir: &Path) -> PathBuf { 508 - let params_def = extract_params_from_raw_route(&self.route_raw()); 509 - build_file_path_with_params( 510 - &self.route_raw(), 528 + let route = self.route_raw(); 529 + let params_def = extract_params_from_raw_route(&route); 530 + build_file_path_with_params(&route, &params_def, params, output_dir, self.is_endpoint()) 531 + } 532 + 533 + fn variant_file_path( 534 + &self, 535 + params: &PageParams, 536 + output_dir: &Path, 537 + variant: &str, 538 + ) -> Result<PathBuf, String> { 539 + let variants = self.variants(); 540 + let variant_path = variants 541 + .iter() 542 + .find(|(id, _)| id == variant) 543 + .map(|(_, path)| path.clone()) 544 + .ok_or_else(|| format!("Variant '{}' not found", variant))?; 545 + let is_endpoint = guess_if_route_is_endpoint(&variant_path); 546 + let params_def = extract_params_from_raw_route(&variant_path); 547 + Ok(build_file_path_with_params( 548 + &variant_path, 511 549 &params_def, 512 550 params, 513 551 output_dir, 514 - self.is_endpoint(), 515 - ) 552 + is_endpoint, 553 + )) 516 554 } 517 555 } 518 556 ··· 528 566 fn url(&self, params: Params) -> String { 529 567 InternalRoute::url(self, &params.into()) 530 568 } 569 + 570 + /// Get the URL for this page with the given parameters and variant 571 + /// 572 + /// Returns an error if the variant does not exist on this route. 573 + /// 574 + /// # Example 575 + /// ```rust,ignore 576 + /// let url = route.variant_url(params, "en")?; 577 + /// ``` 578 + fn variant_url(&self, params: Params, variant: &str) -> Result<String, String> { 579 + InternalRoute::variant_url(self, &params.into(), variant) 580 + } 531 581 } 532 582 533 583 // Blanket implementation for all Page implementors that also implement InternalPage ··· 661 711 inner: &'a dyn FullRoute, 662 712 params_cache: OnceLock<Vec<ParameterDef>>, 663 713 is_endpoint: OnceLock<bool>, 714 + variant_caches: OnceLock<FxHashMap<String, (Vec<ParameterDef>, bool)>>, 664 715 } 665 716 666 717 impl<'a> CachedRoute<'a> { ··· 669 720 inner: route, 670 721 params_cache: OnceLock::new(), 671 722 is_endpoint: OnceLock::new(), 723 + variant_caches: OnceLock::new(), 672 724 } 673 725 } 674 726 ··· 682 734 .is_endpoint 683 735 .get_or_init(|| guess_if_route_is_endpoint(&self.inner.route_raw())) 684 736 } 737 + 738 + fn get_variant_cache(&self, variant_id: &str) -> Option<&(Vec<ParameterDef>, bool)> { 739 + let variant_caches = self.variant_caches.get_or_init(|| { 740 + let mut map = FxHashMap::default(); 741 + for (id, path) in self.inner.variants() { 742 + let params = extract_params_from_raw_route(&path); 743 + let is_endpoint = guess_if_route_is_endpoint(&path); 744 + map.insert(id, (params, is_endpoint)); 745 + } 746 + map 747 + }); 748 + 749 + variant_caches.get(variant_id) 750 + } 685 751 } 686 752 687 753 impl<'a> InternalRoute for CachedRoute<'a> { ··· 721 787 ) 722 788 } 723 789 790 + fn variant_url(&self, params: &PageParams, variant: &str) -> Result<String, String> { 791 + let (params_def, is_endpoint) = self 792 + .get_variant_cache(variant) 793 + .ok_or_else(|| format!("Variant '{}' not found", variant))?; 794 + let variants = self.inner.variants(); 795 + let variant_path = variants 796 + .iter() 797 + .find(|(id, _)| id == variant) 798 + .map(|(_, path)| path.clone()) 799 + .ok_or_else(|| format!("Variant '{}' not found", variant))?; 800 + Ok(build_url_with_params( 801 + &variant_path, 802 + params_def, 803 + params, 804 + *is_endpoint, 805 + )) 806 + } 807 + 724 808 fn file_path(&self, params: &PageParams, output_dir: &Path) -> PathBuf { 725 809 build_file_path_with_params( 726 810 &self.route_raw(), ··· 729 813 output_dir, 730 814 self.is_endpoint(), 731 815 ) 816 + } 817 + 818 + fn variant_file_path( 819 + &self, 820 + params: &PageParams, 821 + output_dir: &Path, 822 + variant: &str, 823 + ) -> Result<PathBuf, String> { 824 + let (params_def, is_endpoint) = self 825 + .get_variant_cache(variant) 826 + .ok_or_else(|| format!("Variant '{}' not found", variant))?; 827 + let variants = self.inner.variants(); 828 + let variant_path = variants 829 + .iter() 830 + .find(|(id, _)| id == variant) 831 + .map(|(_, path)| path.clone()) 832 + .ok_or_else(|| format!("Variant '{}' not found", variant))?; 833 + Ok(build_file_path_with_params( 834 + &variant_path, 835 + params_def, 836 + params, 837 + output_dir, 838 + *is_endpoint, 839 + )) 732 840 } 733 841 } 734 842