1use crate::store::highlights::{AnchorStatus, Highlight};
2
3pub struct BookContext {
4 pub title: String,
5 pub author: Option<String>,
6 pub published: Option<String>,
7 pub progress_pct: Option<f32>,
8 pub source_path: String,
9 pub tags: Vec<String>,
10 pub exported_at: String,
11}
12
13pub fn render(ctx: &BookContext, highs: &[Highlight]) -> String {
14 let mut s = String::new();
15 s.push_str("---\n");
16 s.push_str(&format!("title: {}\n", ctx.title));
17 if let Some(a) = &ctx.author {
18 s.push_str(&format!("author: {a}\n"));
19 }
20 if let Some(p) = &ctx.published {
21 s.push_str(&format!("published: {p}\n"));
22 }
23 s.push_str(&format!("exported: {}\n", ctx.exported_at));
24 if let Some(p) = ctx.progress_pct {
25 s.push_str(&format!("progress: {p:.0}%\n"));
26 }
27 s.push_str(&format!("source: {}\n", ctx.source_path));
28 if !ctx.tags.is_empty() {
29 s.push_str(&format!("tags: [{}]\n", ctx.tags.join(", ")));
30 }
31 s.push_str("---\n\n");
32
33 let mut current_chapter: Option<String> = None;
34 for h in highs {
35 if h.chapter_title.as_deref() != current_chapter.as_deref() {
36 current_chapter = h.chapter_title.clone();
37 if let Some(ch) = ¤t_chapter {
38 s.push_str(&format!("## {ch}\n\n"));
39 }
40 }
41 let marker = if matches!(h.anchor_status, AnchorStatus::Drifted) {
42 " *(drifted)*"
43 } else if matches!(h.anchor_status, AnchorStatus::Lost) {
44 " *(lost)*"
45 } else {
46 ""
47 };
48 s.push_str(&format!("> {}{}\n\n", h.text.replace('\n', " "), marker));
49 if let Some(n) = &h.note {
50 s.push_str(&format!("**Note:** {n}\n\n"));
51 }
52 s.push_str("---\n\n");
53 }
54 s
55}