sara-tasks 2.4.0

Sara — folder-aware task manager
use anyhow::Result;
use rusqlite::Connection;

use crate::commands::recall::enrich::provenance::mark_provenance;
use crate::commands::recall::enrich::stale::mark_stale;
use crate::commands::recall::types::Hit;
use crate::commands::shared::{item_label, item_snippet, short_id};
use crate::infrastructure::db;
use crate::infrastructure::model::Item;

pub(in crate::commands::recall) fn recent_hits(conn: &Connection, limit: i64) -> Result<Vec<Hit>> {
    let mut memories = db::list_memories(conn)?;
    memories.truncate(limit.max(0) as usize);
    let mut hits: Vec<Hit> = memories
        .into_iter()
        .map(|m| item_hit(conn, m, false))
        .collect();
    mark_stale(conn, &mut hits);
    mark_provenance(conn, &mut hits);
    Ok(hits)
}

pub(in crate::commands::recall) fn item_hit(
    conn: &Connection,
    item: Item,
    exact_match: bool,
) -> Hit {
    let handle = item_label(&item);
    let snippet = item_snippet(&item, 160);
    let files = db::get_item_files(conn, &item.uuid).unwrap_or_default();
    let linked_tasks = db::get_item_task_links(conn, &item.uuid).unwrap_or_default();
    let uuid = item.uuid.to_string();
    let links_to = db::get_memory_links_to(conn, &uuid).unwrap_or_default();
    let links_from = db::get_memory_links_from(conn, &uuid).unwrap_or_default();
    let superseded_by = linked_labels(
        conn,
        links_to
            .iter()
            .filter(|l| l.relation == "supersedes")
            .map(|l| l.from_uuid.as_str()),
    );
    let derived_from_labels = linked_labels(
        conn,
        links_from
            .iter()
            .filter(|l| l.relation == "derived_from")
            .map(|l| l.to_uuid.as_str()),
    );
    let derived_children = linked_labels(
        conn,
        links_to
            .iter()
            .filter(|l| l.relation == "derived_from")
            .map(|l| l.from_uuid.as_str()),
    );
    Hit {
        ref_kind: format!("item_{}", item.kind),
        strength: db::item_strength(conn, &item),
        label: handle,
        description: item.title.clone(),
        snippet,
        body: item.body.clone(),
        exact_match,
        modified: Some(item.modified),
        files,
        linked_tasks,
        superseded_by,
        provisional: item.status == "provisional",
        item_uuid: Some(item.uuid),
        derived_from_labels,
        derived_children,
        fts_rank: None,
        loose: false,
        semantic: false,
        cosine: None,
        cluster: None,
        stale: Vec::new(),
        provenance: Default::default(),
    }
}

fn linked_labels<'a>(conn: &Connection, uuids: impl Iterator<Item = &'a str>) -> Vec<String> {
    uuids
        .map(|u| {
            db::get_item_by_uuid(conn, u)
                .ok()
                .map(|i| item_label(&i))
                .unwrap_or_else(|| short_id(u))
        })
        .collect()
}

pub(in crate::commands::recall) fn record_recalled(conn: &Connection, hits: &[Hit]) {
    for u in hits.iter().filter_map(|h| h.item_uuid) {
        let _ = db::record_memory_recall(conn, &u);
    }
}