Skip to main content

dioxus_docs_kit/
config.rs

1//! Builder for constructing a `DocsRegistry`.
2
3use crate::registry::DocsRegistry;
4#[cfg(feature = "highlight")]
5use dioxus_code::Theme;
6use std::collections::HashMap;
7
8/// Theme configuration for the documentation site.
9///
10/// Controls which DaisyUI theme(s) are applied and whether a toggle button is shown.
11#[derive(Clone, Debug, PartialEq)]
12pub struct ThemeConfig {
13    /// The default theme name (must match a DaisyUI theme defined in `tailwind.css`).
14    pub default_theme: String,
15    /// If set, enables a light/dark toggle button. Tuple is `(light_theme, dark_theme)`.
16    pub toggle_themes: Option<(String, String)>,
17    /// localStorage key used to persist the user's theme preference.
18    pub storage_key: String,
19}
20
21/// How rendered code blocks pick their syntax-highlighting theme.
22///
23/// Defaults to [`CodeThemeConfig::Adaptive`] with GitHub Light / Tokyo Night.
24///
25/// Only available with the `highlight` feature (default), which pulls in `dioxus-code`.
26#[cfg(feature = "highlight")]
27#[derive(Clone, Copy, Debug)]
28#[non_exhaustive]
29pub enum CodeThemeConfig {
30    /// Always use this one theme, regardless of the site's light/dark state.
31    Fixed(Theme),
32    /// Pick `light` or `dark` to match the site theme.
33    ///
34    /// When a light/dark toggle is configured (via [`DocsConfig::with_theme_toggle`]),
35    /// the choice tracks the active `data-theme` (not the OS `prefers-color-scheme`),
36    /// so code blocks stay in sync with the toggle. Without a toggle it falls back to
37    /// `prefers-color-scheme`.
38    Adaptive {
39        /// Theme used when the site is in its light state.
40        light: Theme,
41        /// Theme used when the site is in its dark state.
42        dark: Theme,
43    },
44}
45
46#[cfg(feature = "highlight")]
47impl Default for CodeThemeConfig {
48    fn default() -> Self {
49        Self::Adaptive {
50            light: Theme::GITHUB_LIGHT,
51            dark: Theme::TOKYO_NIGHT,
52        }
53    }
54}
55
56/// Builder for constructing a [`DocsRegistry`].
57///
58/// # Example
59///
60/// ```rust,ignore
61/// let registry = DocsConfig::new(nav_json, content_map)
62///     // `.with_openapi()` needs the `openapi` feature (on by default)
63///     .with_openapi("api-reference", spec_yaml)
64///     .with_default_path("getting-started/introduction")
65///     .build();
66/// ```
67pub struct DocsConfig {
68    nav_json: String,
69    content_map: HashMap<&'static str, &'static str>,
70    #[cfg(feature = "openapi")]
71    openapi_specs: Vec<(String, String)>,
72    default_path: Option<String>,
73    api_group_name: Option<String>,
74    theme: Option<ThemeConfig>,
75    #[cfg(feature = "highlight")]
76    code_theme: CodeThemeConfig,
77}
78
79impl DocsConfig {
80    /// Create a new builder from a `_nav.json` string and a content map.
81    ///
82    /// The content map is typically generated by `build.rs` using `include_str!()`.
83    pub fn new(nav_json: &str, content_map: HashMap<&'static str, &'static str>) -> Self {
84        Self {
85            nav_json: nav_json.to_string(),
86            content_map,
87            #[cfg(feature = "openapi")]
88            openapi_specs: Vec::new(),
89            default_path: None,
90            api_group_name: None,
91            theme: None,
92            #[cfg(feature = "highlight")]
93            code_theme: CodeThemeConfig::default(),
94        }
95    }
96
97    /// Add an OpenAPI specification.
98    ///
99    /// - `prefix`: The URL prefix for this spec's endpoints (e.g. "api-reference").
100    /// - `yaml`: The raw YAML string of the OpenAPI spec.
101    ///
102    /// The `prefix` must correspond to a nav group in `_nav.json` whose `"group"` value
103    /// matches [`Self::with_api_group_name`] (defaults to `"API Reference"`). The library
104    /// dynamically injects API endpoints into that group's sidebar — do **not** list
105    /// individual operation paths in the `"pages"` array of `_nav.json`.
106    ///
107    /// Only available with the `openapi` feature (default), which pulls in
108    /// `openapiv3` and `serde_yaml`.
109    #[cfg(feature = "openapi")]
110    pub fn with_openapi(mut self, prefix: &str, yaml: &str) -> Self {
111        self.openapi_specs
112            .push((prefix.to_string(), yaml.to_string()));
113        self
114    }
115
116    /// Set the default documentation path for redirects.
117    ///
118    /// Defaults to the first page in the first nav group if not set.
119    pub fn with_default_path(mut self, path: &str) -> Self {
120        self.default_path = Some(path.to_string());
121        self
122    }
123
124    /// Set the display name for the API Reference sidebar group.
125    ///
126    /// Defaults to `"API Reference"`. The value must match a `"group"` in `_nav.json`
127    /// so the library knows where to inject the API endpoint sidebar entries.
128    /// See [`Self::with_openapi`] for details.
129    pub fn with_api_group_name(mut self, name: &str) -> Self {
130        self.api_group_name = Some(name.to_string());
131        self
132    }
133
134    /// Set a single theme (no toggle button).
135    ///
136    /// The theme name must match a DaisyUI theme defined in the consumer's `tailwind.css`.
137    pub fn with_theme(mut self, theme: &str) -> Self {
138        self.theme = Some(ThemeConfig {
139            default_theme: theme.to_string(),
140            toggle_themes: None,
141            storage_key: "docs-theme".to_string(),
142        });
143        self
144    }
145
146    /// Enable a light/dark theme toggle.
147    ///
148    /// - `light`: Name of the light DaisyUI theme.
149    /// - `dark`: Name of the dark DaisyUI theme.
150    /// - `default`: Which of the two to use on first visit (`light` or `dark`).
151    pub fn with_theme_toggle(mut self, light: &str, dark: &str, default: &str) -> Self {
152        self.theme = Some(ThemeConfig {
153            default_theme: default.to_string(),
154            toggle_themes: Some((light.to_string(), dark.to_string())),
155            storage_key: "docs-theme".to_string(),
156        });
157        self
158    }
159
160    /// Use a single, fixed syntax-highlighting theme for all code blocks.
161    ///
162    /// Use this for single-theme sites (e.g. a dark-only app) so the code block
163    /// background matches the site instead of following the reader's OS setting.
164    ///
165    /// Only available with the `highlight` feature (default), which pulls in `dioxus-code`.
166    #[cfg(feature = "highlight")]
167    pub fn with_code_theme(mut self, theme: Theme) -> Self {
168        self.code_theme = CodeThemeConfig::Fixed(theme);
169        self
170    }
171
172    /// Use a light/dark pair of syntax themes for code blocks.
173    ///
174    /// When a theme toggle is configured (see [`Self::with_theme_toggle`]), the active
175    /// choice tracks the toggle's `data-theme`; otherwise it follows the reader's OS
176    /// `prefers-color-scheme`. Defaults to GitHub Light / Tokyo Night when not set.
177    ///
178    /// Only available with the `highlight` feature (default), which pulls in `dioxus-code`.
179    #[cfg(feature = "highlight")]
180    pub fn with_code_themes(mut self, light: Theme, dark: Theme) -> Self {
181        self.code_theme = CodeThemeConfig::Adaptive { light, dark };
182        self
183    }
184
185    /// Build the [`DocsRegistry`].
186    ///
187    /// Parses all documents, builds the search index, and parses OpenAPI specs.
188    ///
189    /// # Panics
190    ///
191    /// Panics with a descriptive message if `_nav.json` or an OpenAPI spec fails
192    /// to parse. Use [`Self::try_build`] to handle these errors yourself.
193    pub fn build(self) -> DocsRegistry {
194        self.try_build()
195            .unwrap_or_else(|e| panic!("dioxus-docs-kit: {e}"))
196    }
197
198    /// Build the [`DocsRegistry`], returning an error instead of panicking when
199    /// `_nav.json` or an OpenAPI spec fails to parse.
200    pub fn try_build(self) -> Result<DocsRegistry, crate::error::DocsKitError> {
201        DocsRegistry::try_from_config(self)
202    }
203
204    // Accessors for DocsRegistry::from_config
205    pub(crate) fn nav_json(&self) -> &str {
206        &self.nav_json
207    }
208
209    pub(crate) fn content_map(&self) -> &HashMap<&'static str, &'static str> {
210        &self.content_map
211    }
212
213    #[cfg(feature = "openapi")]
214    pub(crate) fn openapi_specs(&self) -> &[(String, String)] {
215        &self.openapi_specs
216    }
217
218    pub(crate) fn default_path_value(&self) -> Option<&str> {
219        self.default_path.as_deref()
220    }
221
222    pub(crate) fn api_group_name_value(&self) -> Option<&str> {
223        self.api_group_name.as_deref()
224    }
225
226    pub(crate) fn theme_config(&self) -> Option<&ThemeConfig> {
227        self.theme.as_ref()
228    }
229
230    #[cfg(feature = "highlight")]
231    pub(crate) fn code_theme_value(&self) -> CodeThemeConfig {
232        self.code_theme
233    }
234}