verso/store/
highlights.rs1use rusqlite::{params, Connection};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum AnchorStatus {
5 Ok,
6 Drifted,
7 Lost,
8}
9
10impl AnchorStatus {
11 pub fn as_str(self) -> &'static str {
12 match self {
13 Self::Ok => "ok",
14 Self::Drifted => "drifted",
15 Self::Lost => "lost",
16 }
17 }
18 pub fn parse(s: &str) -> Self {
19 match s {
20 "drifted" => Self::Drifted,
21 "lost" => Self::Lost,
22 _ => Self::Ok,
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
28pub struct Highlight {
29 pub id: i64,
30 pub book_id: i64,
31 pub spine_idx: u32,
32 pub chapter_title: Option<String>,
33 pub char_offset_start: u64,
34 pub char_offset_end: u64,
35 pub text: String,
36 pub context_before: Option<String>,
37 pub context_after: Option<String>,
38 pub note: Option<String>,
39 pub anchor_status: AnchorStatus,
40}
41
42pub fn insert(c: &mut Connection, h: &Highlight) -> anyhow::Result<i64> {
43 c.execute(
44 "INSERT INTO highlights(book_id, spine_idx, chapter_title, char_offset_start, char_offset_end,
45 text, context_before, context_after, note, anchor_status)
46 VALUES (?,?,?,?,?,?,?,?,?,?)",
47 params![h.book_id, h.spine_idx, h.chapter_title, h.char_offset_start, h.char_offset_end,
48 h.text, h.context_before, h.context_after, h.note, h.anchor_status.as_str()],
49 )?;
50 Ok(c.last_insert_rowid())
51}
52
53pub fn delete(c: &mut Connection, id: i64) -> anyhow::Result<()> {
54 c.execute("DELETE FROM highlights WHERE id = ?", params![id])?;
55 Ok(())
56}
57
58pub fn list(c: &Connection, book_id: i64) -> anyhow::Result<Vec<Highlight>> {
59 let mut stmt = c.prepare(
60 "SELECT id, book_id, spine_idx, chapter_title, char_offset_start, char_offset_end,
61 text, context_before, context_after, note, anchor_status
62 FROM highlights WHERE book_id = ? ORDER BY spine_idx, char_offset_start",
63 )?;
64 let rows: Vec<Highlight> = stmt
65 .query_map(params![book_id], |r| {
66 Ok(Highlight {
67 id: r.get(0)?,
68 book_id: r.get(1)?,
69 spine_idx: r.get(2)?,
70 chapter_title: r.get(3)?,
71 char_offset_start: r.get(4)?,
72 char_offset_end: r.get(5)?,
73 text: r.get(6)?,
74 context_before: r.get(7)?,
75 context_after: r.get(8)?,
76 note: r.get(9)?,
77 anchor_status: AnchorStatus::parse(&r.get::<_, String>(10)?),
78 })
79 })?
80 .collect::<Result<_, _>>()?;
81 Ok(rows)
82}