src/warehouse/access_scan.rs
//! The **fn → warehouse-table access fact**, as the architecture board reads it.
//!
//! This is the one wiring edge no call graph yields: *which function
//! reads/writes which warehouse table*. The fact itself is the code room's
//! (`TOUCHES_TABLE`, written by the rustc driver where a table name meets a
//! read or a write of the storage engine — `crates/mimir-driver`,
//! `walk/tables.rs`); [`AccessEdge`] is the row shape the board
//! ([`crate::arch`]) folds, filled by `code_room::reader` from those edges.
//!
//! Nothing here finds an access: the `syn` walker and the closed map of
//! accessor NAMES (`query_<table>` / `append_<table>` …) that classified its
//! call sites went with Mímir v2 (PLAN-MIMIR-V2 §7.3b).
/// `read` or `write` — the direction of a warehouse-table access.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
pub enum Access {
Read,
Write,
}
impl Access {
pub fn as_str(self) -> &'static str {
match self {
Access::Read => "read",
Access::Write => "write",
}
}
}
/// A table name the board skips: an access whose table is not known at
/// compile time has no Table node. (The code room keeps those sites — its
/// `<unresolved>` table node — and the readers count them; the reader hands
/// the board only rows with a real name.)
pub const DYNAMIC_TABLE: &str = "<dynamic>";
/// One extracted fact: `caller_fn` (in `crate`) does a `access` on `table` at
/// `file:line`.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub struct AccessEdge {
pub caller_fn: String,
pub crate_name: String,
pub table: String,
pub access: Access,
pub file: String,
pub line: u32,
}
impl AccessEdge {
/// Sort/dedup key (stable order independent of walk order).
pub fn key(&self) -> (String, String, String, &'static str, u32) {
(
self.caller_fn.clone(),
self.crate_name.clone(),
self.table.clone(),
self.access.as_str(),
self.line,
)
}
}