mirror_log/view/
mod.rs

1use chrono::{DateTime, TimeZone, Utc};
2use rusqlite::{Connection, Result};
3
4#[derive(Debug)]
5pub struct Event {
6    pub id: String,
7    pub timestamp: i64,
8    pub source: String,
9    pub content: String,
10    pub meta: Option<String>,
11}
12
13impl Event {
14    pub fn format_time(&self) -> String {
15        let dt: DateTime<Utc> = Utc.timestamp_opt(self.timestamp, 0).unwrap();
16        dt.format("%Y-%m-%d %H:%M:%S UTC").to_string()
17    }
18
19    pub fn preview_content(&self, max_chars: usize) -> String {
20        if self.content.len() <= max_chars {
21            self.content.clone()
22        } else {
23            format!(
24                "{}...\n\n[Content truncated: {} of {} chars shown]",
25                &self.content[..max_chars],
26                max_chars,
27                self.content.len()
28            )
29        }
30    }
31}
32
33pub fn recent(conn: &Connection, limit: i64) -> Result<Vec<Event>> {
34    let mut stmt = conn.prepare(
35        "SELECT id, timestamp, source, content, meta
36         FROM events
37         ORDER BY timestamp DESC
38         LIMIT ?1",
39    )?;
40
41    let rows = stmt.query_map([limit], |row| {
42        Ok(Event {
43            id: row.get(0)?,
44            timestamp: row.get(1)?,
45            source: row.get(2)?,
46            content: row.get(3)?,
47            meta: row.get(4)?,
48        })
49    })?;
50
51    Ok(rows.filter_map(Result::ok).collect())
52}
53
54pub fn search(conn: &Connection, term: &str) -> Result<Vec<Event>> {
55    let like = format!("%{}%", term);
56
57    let mut stmt = conn.prepare(
58        "SELECT id, timestamp, source, content, meta
59         FROM events
60         WHERE content LIKE ?1
61         ORDER BY timestamp DESC",
62    )?;
63
64    let rows = stmt.query_map([like], |row| {
65        Ok(Event {
66            id: row.get(0)?,
67            timestamp: row.get(1)?,
68            source: row.get(2)?,
69            content: row.get(3)?,
70            meta: row.get(4)?,
71        })
72    })?;
73
74    Ok(rows.filter_map(Result::ok).collect())
75}
76
77pub fn by_source(conn: &Connection, source: &str, limit: Option<i64>) -> Result<Vec<Event>> {
78    let query = if let Some(lim) = limit {
79        format!(
80            "SELECT id, timestamp, source, content, meta
81             FROM events
82             WHERE source = ?1
83             ORDER BY timestamp DESC
84             LIMIT {}",
85            lim
86        )
87    } else {
88        "SELECT id, timestamp, source, content, meta
89         FROM events
90         WHERE source = ?1
91         ORDER BY timestamp DESC"
92            .to_string()
93    };
94
95    let mut stmt = conn.prepare(&query)?;
96
97    let rows = stmt.query_map([source], |row| {
98        Ok(Event {
99            id: row.get(0)?,
100            timestamp: row.get(1)?,
101            source: row.get(2)?,
102            content: row.get(3)?,
103            meta: row.get(4)?,
104        })
105    })?;
106
107    Ok(rows.filter_map(Result::ok).collect())
108}
109
110pub fn get_by_id(conn: &Connection, id: &str) -> Result<Event> {
111    conn.query_row(
112        "SELECT id, timestamp, source, content, meta
113         FROM events
114         WHERE id = ?1",
115        [id],
116        |row| {
117            Ok(Event {
118                id: row.get(0)?,
119                timestamp: row.get(1)?,
120                source: row.get(2)?,
121                content: row.get(3)?,
122                meta: row.get(4)?,
123            })
124        },
125    )
126}