dioxus_docs_kit/blog/
types.rs1use dioxus_mdx::DocNode;
2use serde::Deserialize;
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Deserialize)]
7pub struct BlogManifest {
8 #[serde(default)]
9 pub authors: HashMap<String, Author>,
10 pub posts: Vec<String>,
11}
12
13#[derive(Debug, Clone, PartialEq, Deserialize)]
15pub struct Author {
16 pub name: String,
17 #[serde(default)]
18 pub avatar: Option<String>,
19 #[serde(default)]
20 pub bio: Option<String>,
21 #[serde(default)]
22 pub url: Option<String>,
23}
24
25#[derive(Debug, Clone, PartialEq, Deserialize)]
27pub struct BlogFrontmatter {
28 pub title: String,
29 #[serde(default)]
30 pub description: Option<String>,
31 pub date: String,
33 pub author: String,
35 #[serde(default)]
36 pub tags: Vec<String>,
37 #[serde(default, rename = "coverImage")]
39 pub cover_image: Option<String>,
40 #[serde(default)]
42 pub draft: bool,
43 #[serde(default)]
45 pub featured: bool,
46}
47
48#[derive(Debug, Clone, PartialEq)]
50pub struct BlogPost {
51 pub slug: String,
53 pub frontmatter: BlogFrontmatter,
54 pub content: Vec<DocNode>,
56 pub raw_markdown: String,
58 pub reading_time_minutes: u32,
60}
61
62#[derive(PartialEq)]
64pub struct BlogSearchEntry {
65 pub slug: String,
66 pub title: String,
67 pub description: String,
68 pub content_preview: String,
69 pub date: String,
70 pub tags: Vec<String>,
71}
72
73pub fn extract_blog_frontmatter(content: &str) -> Option<(BlogFrontmatter, &str)> {
77 let content = content.trim();
78
79 if !content.starts_with("---") {
80 return None;
81 }
82
83 let after_first_delim = &content[3..];
84 let end_idx = after_first_delim.find("\n---")?;
85 let yaml_content = after_first_delim[..end_idx].trim();
86 let remaining = after_first_delim[end_idx + 4..].trim_start();
87
88 let fm: BlogFrontmatter = serde_yaml::from_str(yaml_content).ok()?;
89 Some((fm, remaining))
90}
91
92pub fn calculate_reading_time(text: &str) -> u32 {
94 let word_count = text.split_whitespace().count();
95 ((word_count as f64 / 200.0).ceil() as u32).max(1)
96}