use dioxus_mdx::DocNode;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Debug, Clone, Deserialize)]
pub struct BlogManifest {
#[serde(default)]
pub authors: HashMap<String, Author>,
pub posts: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct Author {
pub name: String,
#[serde(default)]
pub avatar: Option<String>,
#[serde(default)]
pub bio: Option<String>,
#[serde(default)]
pub url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct BlogFrontmatter {
pub title: String,
#[serde(default)]
pub description: Option<String>,
pub date: String,
pub author: String,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default, rename = "coverImage")]
pub cover_image: Option<String>,
#[serde(default)]
pub draft: bool,
#[serde(default)]
pub featured: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BlogPost {
pub slug: String,
pub frontmatter: BlogFrontmatter,
pub content: Vec<DocNode>,
pub raw_markdown: String,
pub reading_time_minutes: u32,
}
#[derive(PartialEq)]
pub struct BlogSearchEntry {
pub slug: String,
pub title: String,
pub description: String,
pub content_preview: String,
pub date: String,
pub tags: Vec<String>,
}
pub fn extract_blog_frontmatter(content: &str) -> Option<(BlogFrontmatter, &str)> {
let content = content.trim();
if !content.starts_with("---") {
return None;
}
let after_first_delim = &content[3..];
let end_idx = after_first_delim.find("\n---")?;
let yaml_content = after_first_delim[..end_idx].trim();
let remaining = after_first_delim[end_idx + 4..].trim_start();
let fm: BlogFrontmatter = serde_yaml::from_str(yaml_content).ok()?;
Some((fm, remaining))
}
pub fn calculate_reading_time(text: &str) -> u32 {
let word_count = text.split_whitespace().count();
((word_count as f64 / 200.0).ceil() as u32).max(1)
}