Skip to main content

dioxus_docs_kit/blog/
config.rs

1//! Builder for constructing a `BlogRegistry`.
2
3use crate::blog::registry::BlogRegistry;
4use crate::config::ThemeConfig;
5use std::collections::HashMap;
6
7/// Builder for constructing a [`BlogRegistry`].
8///
9/// # Example
10///
11/// ```rust,ignore
12/// let registry = BlogConfig::new(include_str!("../blog/_blog.json"), blog_content_map())
13///     .with_posts_per_page(9)
14///     .with_theme_toggle("light", "dark", "dark")
15///     .build();
16/// ```
17pub struct BlogConfig {
18    manifest_json: String,
19    content_map: HashMap<&'static str, &'static str>,
20    posts_per_page: usize,
21    date_format: String,
22    theme: Option<ThemeConfig>,
23    category_base_path: Option<String>,
24}
25
26impl BlogConfig {
27    /// Create a new builder from a `_blog.json` string and a content map.
28    pub fn new(manifest_json: &str, content_map: HashMap<&'static str, &'static str>) -> Self {
29        Self {
30            manifest_json: manifest_json.to_string(),
31            content_map,
32            posts_per_page: 9,
33            date_format: "%B %d, %Y".to_string(),
34            theme: None,
35            category_base_path: None,
36        }
37    }
38
39    /// Set the number of posts per page for pagination (default: 9).
40    pub fn with_posts_per_page(mut self, n: usize) -> Self {
41        self.posts_per_page = n;
42        self
43    }
44
45    /// Enable linkable tag categories under a root-relative path.
46    ///
47    /// Register routes for `{base}/:slug` and `{base}/:slug/page/:page`
48    /// rendering [`crate::BlogCategoryPage`] (page numbers start at one).
49    /// With this unset, existing tag buttons retain their local filtering behavior
50    /// and no category URLs are emitted into the sitemap.
51    pub fn with_category_base_path(mut self, path: &str) -> Self {
52        self.category_base_path = Some(path.trim_end_matches('/').to_string());
53        self
54    }
55
56    pub(crate) fn category_base_path(&self) -> Option<&str> {
57        self.category_base_path.as_deref()
58    }
59
60    /// Set the date display format (default: "%B %d, %Y").
61    pub fn with_date_format(mut self, fmt: &str) -> Self {
62        self.date_format = fmt.to_string();
63        self
64    }
65
66    /// Set a single theme (no toggle button).
67    pub fn with_theme(mut self, theme: &str) -> Self {
68        self.theme = Some(ThemeConfig {
69            default_theme: theme.to_string(),
70            toggle_themes: None,
71            storage_key: "docs-theme".to_string(),
72        });
73        self
74    }
75
76    /// Enable a light/dark theme toggle.
77    pub fn with_theme_toggle(mut self, light: &str, dark: &str, default: &str) -> Self {
78        self.theme = Some(ThemeConfig {
79            default_theme: default.to_string(),
80            toggle_themes: Some((light.to_string(), dark.to_string())),
81            storage_key: "docs-theme".to_string(),
82        });
83        self
84    }
85
86    /// Build the [`BlogRegistry`].
87    ///
88    /// # Panics
89    ///
90    /// Panics with a descriptive message if `_blog.json` fails to parse.
91    /// Use [`Self::try_build`] to handle the error yourself.
92    pub fn build(self) -> BlogRegistry {
93        self.try_build()
94            .unwrap_or_else(|e| panic!("dioxus-docs-kit: {e}"))
95    }
96
97    /// Build the [`BlogRegistry`], returning an error instead of panicking when
98    /// `_blog.json` fails to parse.
99    pub fn try_build(self) -> Result<BlogRegistry, crate::error::DocsKitError> {
100        BlogRegistry::try_from_config(self)
101    }
102
103    pub(crate) fn manifest_json(&self) -> &str {
104        &self.manifest_json
105    }
106
107    pub(crate) fn content_map(&self) -> &HashMap<&'static str, &'static str> {
108        &self.content_map
109    }
110
111    pub(crate) fn posts_per_page(&self) -> usize {
112        self.posts_per_page
113    }
114
115    pub(crate) fn date_format(&self) -> &str {
116        &self.date_format
117    }
118
119    pub(crate) fn theme_config(&self) -> Option<&ThemeConfig> {
120        self.theme.as_ref()
121    }
122}