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/room_feed/fixture/groups.rs

//! The fixture of `group_twins` on the compiled graph: one repo `grp`
//! (crate grp@0.1.0) whose dump carries what the driver writes BELOW a
//! function — CALLS and READS_FIELD edges with one site per line, and `local`
//! parts with their inferred types and lines.
//!
//! ```text
//! src/lib.rs
//!   Ledger { entries, seed }
//!   summarise  ─┐ ONE compiled group: the same resolved callees, fields and let
//!   rollup     ─┘ types, line for line. rollup's copy is written in call
//!                 syntax the other way round (`Ledger::weigh(book, item)` vs
//!                 `ledger.weigh(e)`, `u64::wrapping_mul(roll, …)` vs
//!                 `carry.wrapping_mul(…)`), its locals are renamed, and two
//!                 unrelated statements are INTERLEAVED into it — the source
//!                 text normalises to different bodies; the graph sees one group
//!   bg0..bg29     the common case: one let, one own callee, `Vec::push`
//! ```
//!
//! The checkout is written at `<tmp>/git/grp`, where the source lenses look
//! for it. The warehouse holds NO embeddings table.

use std::collections::BTreeSet;
use std::path::PathBuf;

use mimir_ingest::CommitDump;
use mimir_schema::{EdgeKind, EdgeRow, Level, Origin, SpanRow, moniker};

use super::vectors::{Source, consume_next};
use super::{N, Workspace, e, n, site, span, unit};
use crate::code_room::CodeRoom;
use crate::warehouse::iceberg::IcebergWarehouse;

pub const GRP: &str = "grp";

pub fn grp(d: &str) -> String {
    moniker::moniker("grp", "0.1.0", d)
}

/// A callee of the dependency `dep` (a reference stub).
fn dep(d: &str) -> String {
    moniker::moniker("dep", "1.2.3", d)
}

/// One body line: its text and the compiled facts on it.
struct Line {
    text: &'static str,
    calls: Vec<String>,
    fields: Vec<&'static str>,
    /// `(name, type)` of a `let` on this line.
    local: Option<(&'static str, &'static str)>,
}

fn line(text: &'static str, calls: &[String], fields: &[&'static str], local: Option<(&'static str, &'static str)>) -> Line {
    Line { text, calls: calls.to_vec(), fields: fields.to_vec(), local }
}

fn open() -> String {
    dep("impl#[Store]open().")
}
fn weigh() -> String {
    dep("impl#[Store]weigh().")
}
fn sort_by_weight() -> String {
    dep("impl#[Store]sort_by_weight().")
}
fn core(d: &str) -> String {
    moniker::moniker("core", "1.0.0", d)
}

/// The planted group as `summarise` writes it.
fn summarise() -> Vec<Line> {
    let (wm, push, len) = (core("impl#[u64]wrapping_mul()."), core("impl#[Vec]push()."), core("impl#[Vec]len()."));
    vec![
        line("pub fn summarise(ledger: &Ledger) -> u64 {", &[], &[], None),
        line("    let mut acc: Vec<(String, u64)> = Vec::new();", &[], &[], Some(("acc", "Vec<(String, u64)>"))),
        line("    let mut carry: u64 = 7;", &[], &[], Some(("carry", "u64"))),
        line("    for e in ledger.open().entries.iter() {", &[open()], &["entries"], None),
        line("        let w = ledger.weigh(e) + carry;", &[weigh()], &[], Some(("w", "u64"))),
        line("        carry = carry.wrapping_mul(ledger.seed);", &[wm], &["seed"], None),
        line("        acc.push((e.to_string(), w));", &[push], &[], None),
        line("    }", &[], &[], None),
        line("    ledger.sort_by_weight(&mut acc, &ledger.entries);", &[sort_by_weight()], &["entries"], None),
        line("    acc.len() as u64", &[len], &[], None),
        line("}", &[], &[], None),
    ]
}

/// The same group in `rollup`: other call syntax, renamed locals, two
/// unrelated statements interleaved (lines 4 and 7 of the item).
fn rollup() -> Vec<Line> {
    let (wm, push, len) = (core("impl#[u64]wrapping_mul()."), core("impl#[Vec]push()."), core("impl#[Vec]len()."));
    vec![
        line("pub fn rollup(book: &Ledger, route: &str) -> u64 {", &[], &[], None),
        line("    let mut bucket: Vec<(String, u64)> = Vec::new();", &[], &[], Some(("bucket", "Vec<(String, u64)>"))),
        line("    let mut roll: u64 = 9;", &[], &[], Some(("roll", "u64"))),
        line("    log_route(route);", &[dep("log_route().")], &[], None),
        line("    for item in Store::open(book).entries.iter() {", &[open()], &["entries"], None),
        line("        let weight = Store::weigh(book, item) + roll;", &[weigh()], &[], Some(("weight", "u64"))),
        line("        let banner = route.to_ascii_uppercase();", &[core("impl#[str]to_ascii_uppercase().")], &[], Some(("banner", "String"))),
        line("        roll = u64::wrapping_mul(roll, book.seed);", &[wm], &["seed"], None),
        line("        Vec::push(&mut bucket, (item.to_string(), weight));", &[push], &[], None),
        line("    }", &[], &[], None),
        line("    Store::sort_by_weight(book, &mut bucket, &book.entries);", &[sort_by_weight()], &["entries"], None),
        line("    Vec::len(&bucket) as u64", &[len], &[], None),
        line("}", &[], &[], None),
    ]
}

/// The common case: `bg{i}` binds a `u64`, calls its own `step{i}`, pushes.
fn background(i: usize) -> (String, Vec<Line>) {
    let text = |s: String| -> &'static str { Box::leak(s.into_boxed_str()) };
    let name = format!("bg{i}");
    let lines = vec![
        line(text(format!("pub fn {name}(x: u64, out: &mut Vec<u64>) {{")), &[], &[], None),
        line("    let y: u64 = x + 1;", &[], &[], Some(("y", "u64"))),
        line(text(format!("    step{i}(y);")), &[dep(&format!("step{i}()."))], &[], None),
        line("    out.push(y);", &[core("impl#[Vec]push().")], &[], None),
        line("}", &[], &[], None),
    ];
    (name, lines)
}

fn commit(root: &std::path::Path) -> CommitDump {
    let krate = grp("crate/");
    let strukt = grp("Ledger#");
    let field = |f: &str| grp(&format!("Ledger#{f}."));
    let mut fns: Vec<(String, Vec<Line>)> = vec![("summarise".into(), summarise()), ("rollup".into(), rollup())];
    fns.extend((0..30).map(background));

    let mut src = Source::default();
    src.plain("//! grp\n\npub struct Ledger {\n    pub entries: Vec<u64>,\n    pub seed: u64,\n}\n");
    for (name, lines) in &fns {
        let text: Vec<&str> = lines.iter().map(|l| l.text).collect();
        src.item(&grp(&format!("{name}().")), 0, &text.join("\n"));
    }
    let mut spans: Vec<SpanRow> = src.write(root, "src/lib.rs");
    let starts: std::collections::BTreeMap<String, u32> = spans.iter().map(|s| (s.key.clone(), s.start_line)).collect();

    let mut nodes: Vec<N> = vec![
        n(krate.clone(), "crate", Level::Crate, "grp", None),
        n(strukt.clone(), "struct", Level::Item, "Ledger", Some(krate.clone())),
        n(field("entries"), "field", Level::Part, "entries", Some(strukt.clone())),
        n(field("seed"), "field", Level::Part, "seed", Some(strukt.clone())),
    ];
    let mut edges: Vec<EdgeRow> = vec![e(&krate, &strukt, EdgeKind::Contains, 1), e(&strukt, &field("entries"), EdgeKind::Contains, 1), e(&strukt, &field("seed"), EdgeKind::Contains, 1)];
    let mut stubs: BTreeSet<String> = BTreeSet::new();
    for (name, lines) in &fns {
        let id = grp(&format!("{name}()."));
        let start = starts[&id];
        nodes.push(n(id.clone(), "fn", Level::Item, name, Some(krate.clone())));
        edges.push(e(&krate, &id, EdgeKind::Contains, 1));
        // one edge per (target, kind), one site per line it is on
        let mut sites: std::collections::BTreeMap<(String, EdgeKind), Vec<u32>> = Default::default();
        for (i, l) in lines.iter().enumerate() {
            let at = start + i as u32;
            for c in &l.calls {
                stubs.insert(c.clone());
                sites.entry((c.clone(), EdgeKind::Calls)).or_default().push(at);
            }
            for f in &l.fields {
                sites.entry((field(f), EdgeKind::ReadsField)).or_default().push(at);
            }
            if let Some((local, ty)) = l.local {
                let mut lid = id.clone();
                moniker::push_descriptor(&mut lid, moniker::Suffix::Term, &format!("{local}@{at}"));
                nodes.push(n(lid.clone(), "local", Level::Part, local, Some(id.clone())).ty(ty));
                edges.push(e(&id, &lid, EdgeKind::Contains, 1));
                spans.push(span(&lid, "src/lib.rs", at, at));
            }
        }
        for ((to, kind), at) in sites {
            edges.push(e(&id, &to, kind, at.len() as u32));
            for l in at {
                spans.push(site(&id, &to, kind, "src/lib.rs", l));
            }
        }
    }
    for s in &stubs {
        let name = s.rsplit(['#', '/']).next().unwrap_or(s).trim_end_matches("().").to_string();
        nodes.push(n(s.clone(), "fn", Level::Item, &name, None).stub(Origin::Library));
    }
    CommitDump {
        toolchain: "nightly-fixture".into(),
        workspace_crates: ["grp@0.1.0:lib".to_string()].into(),
        workspace_targets: ["grp:lib".to_string()].into(),
        failed_units: BTreeSet::new(),
        owner_disambiguations: 0,
        crates: vec![unit("grp@0.1.0:lib", nodes, edges, spans)],
        binary: false,
        binaries: vec![],
    }
}

/// The workspace and its checkout.
pub struct Grp {
    pub ws: Workspace,
    root: PathBuf,
}

impl Grp {
    /// `<tmp>/git/grp` — where the source lenses look.
    pub fn checkout(&self) -> PathBuf {
        self.root.join("git").join(GRP)
    }
}

/// `grp` consumed at its first commit; no shape, no vector, no embeddings table.
pub fn build() -> Grp {
    let tmp = tempfile::tempdir().unwrap();
    let root = tmp.path().to_path_buf();
    let builds = root.join("builds");
    std::fs::create_dir_all(&builds).unwrap();
    let warehouse = IcebergWarehouse::open(&builds).unwrap();
    let room = CodeRoom::new("groups", &builds);
    let dir = root.join("git").join(GRP);
    nornir_git::gitio::init(&dir).unwrap();
    let dump = commit(&dir);
    nornir_git::gitio::commit_all(&dir, "first").unwrap();
    consume_next(&room, &dir, GRP, dump);
    Grp { ws: Workspace { room, warehouse, builds, _tmp: tmp }, root }
}