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}