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