Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

src/mimir_lens/sources.rs

//! **Provenance-carrying table set for the Mimir lenses** — the anti-silent-empty
//! record. LIFTED from `dwarves::mimir` (mod.rs); the fold logic is unchanged.
//!
//! An empty ranked list and an unfed table must NEVER draw the same. Every lens
//! response carries [`evidence_note`], which names missing feeds by table.

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use super::rows::Table;

/// One warehouse table, plus **which root it was read from** — provenance is a
/// column, not a footnote.
#[derive(Debug, Clone, Default)]
pub struct SourcedTable {
    pub table: Table,
    /// The warehouse root this table's rows came from (`""` when absent everywhere).
    pub root: String,
}

/// The tables a lens reads, merged across one or more warehouse roots.
///
/// Merging is per TABLE, not per row: the first root that has rows for a table
/// wins, and the winning root is recorded. (Since the §2a fix unified the two
/// warehouse roots, nornir reads a single root — but the LIST shape is kept so a
/// consumer that still reads a default root alongside a workspace one stays
/// traceable.)
#[derive(Debug, Clone, Default)]
pub struct Sources {
    tables: BTreeMap<String, SourcedTable>,
    /// Every root that was consulted, in order.
    pub roots: Vec<String>,
}

impl Sources {
    /// Merge a root's tables in. Later roots only fill tables earlier roots left empty.
    pub fn add_root(&mut self, root: impl Into<String>, tables: BTreeMap<String, Table>) {
        let root = root.into();
        self.roots.push(root.clone());
        for (name, t) in tables {
            let slot = self.tables.entry(name).or_default();
            if slot.table.is_empty() && !t.is_empty() {
                slot.table = t;
                slot.root = root.clone();
            }
        }
    }

    pub fn get(&self, table: &str) -> &Table {
        static EMPTY: std::sync::OnceLock<Table> = std::sync::OnceLock::new();
        self.tables
            .get(table)
            .map(|s| &s.table)
            .unwrap_or_else(|| EMPTY.get_or_init(Table::default))
    }

    /// Which root a table's rows came from (`""` ⇒ the table is empty everywhere).
    pub fn root_of(&self, table: &str) -> &str {
        self.tables
            .get(table)
            .map(|s| s.root.as_str())
            .unwrap_or("")
    }

    /// The honest feed record for a set of tables: rows read and the root they came from.
    pub fn feeds(&self, want: &[&str]) -> Vec<Feed> {
        want.iter()
            .map(|t| Feed {
                table: (*t).to_string(),
                rows: self.get(t).rows.len(),
                root: self.root_of(t).to_string(),
                missing: self.get(t).is_empty(),
            })
            .collect()
    }
}

/// What one table actually yielded — the anti-silent-empty record, with provenance.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Feed {
    pub table: String,
    pub rows: usize,
    /// The warehouse root the rows came from.
    pub root: String,
    pub missing: bool,
}

/// A one-line statement of what the evidence supports, rendered verbatim by every
/// front door so no consumer can present a thin result as a strong one.
pub fn evidence_note(feeds: &[Feed], what: &str) -> String {
    let missing: Vec<&str> = feeds
        .iter()
        .filter(|f| f.missing)
        .map(|f| f.table.as_str())
        .collect();
    if missing.is_empty() {
        return format!("{what}: every input table carried rows.");
    }
    format!(
        "{what}: MISSING FEEDS — {} carried NO rows. This is a missing producer, not an \
         absence of findings; any count below is a LOWER bound.",
        missing.join(", ")
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    fn tbl(n: usize) -> Table {
        Table::new(
            vec!["a".into()],
            (0..n).map(|i| vec![i.to_string()]).collect(),
        )
    }

    #[test]
    fn a_table_absent_everywhere_is_a_missing_feed_not_a_clean_result() {
        let mut s = Sources::default();
        s.add_root("/only", [("call_edges".to_string(), tbl(2))].into());
        assert!(s.get("scip_occurrences").is_empty());
        assert_eq!(s.root_of("scip_occurrences"), "");

        let feeds = s.feeds(&["call_edges", "scip_occurrences"]);
        assert!(!feeds[0].missing && feeds[0].rows == 2);
        assert!(feeds[1].missing);
        let note = evidence_note(&feeds, "zombie");
        assert!(note.contains("MISSING FEEDS"), "{note}");
        assert!(note.contains("scip_occurrences"), "{note}");
        assert!(note.contains("LOWER bound"), "{note}");
    }
}