Skip to main content

core_api/repograph/
rules.rs

1//! Rule definitions more than one write path needs to agree on.
2//!
3//! `structure::ensure_rules_and_fulltext` (CLI, `ingest-git`/`sync`) and
4//! [`remember`](super::remember::remember) both write nodes whose derived
5//! edges depend on an `about_<label>` rule existing — the first as part of a
6//! full refresh, the second one `Note` at a time, possibly the very first
7//! write a store ever sees. Two independent copies of these definitions
8//! would eventually drift; this module is the one place they are declared,
9//! so both callers create byte-identical [`RuleDef`]s under the same names.
10
11use core_rules::{default_max_edges, Predicate, RuleDef};
12
13/// Labels a `Note.about` entry may point at, in rule-creation order. One
14/// `about_<label>` rule per label, since a rule has a single destination.
15pub const ABOUT_LABELS: [&str; 5] = ["Author", "Concept", "File", "Note", "Symbol"];
16
17/// The `about_<label>` rule for one of [`ABOUT_LABELS`]: `Note.about` →
18/// `ABOUT` edges to `label`.
19#[must_use]
20pub fn about_rule(label: &str) -> RuleDef {
21    key_rule(
22        &format!("about_{}", label.to_lowercase()),
23        "Note",
24        label,
25        "about",
26        "ABOUT",
27    )
28}
29
30/// `Concept.source_files` → `DESCRIBED_IN` edges to `File`, the rule that
31/// backs [`stale_concepts`](super::stale_concepts)'s provenance.
32#[must_use]
33pub fn concept_sources_rule() -> RuleDef {
34    key_rule(
35        "concept_sources",
36        "Concept",
37        "File",
38        "source_files",
39        "DESCRIBED_IN",
40    )
41}
42
43/// A `KeyMatch` rule with the engine's default fan-out for the predicate,
44/// stated rather than left implicit — the convention across this crate.
45fn key_rule(name: &str, src: &str, dst: &str, field: &str, edge: &str) -> RuleDef {
46    let predicate = Predicate::KeyMatch {
47        field: field.into(),
48    };
49    let max_edges = Some(default_max_edges(&predicate));
50    RuleDef {
51        name: name.into(),
52        src_label: src.into(),
53        dst_label: dst.into(),
54        predicate,
55        edge_type: edge.into(),
56        weight_prop: None,
57        max_edges,
58        approximate: false,
59        via_label: None,
60        via_edge: None,
61        via_dir: None,
62        namespace: None,
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn about_rule_names_and_shapes_match_one_label_per_call() {
72        let r = about_rule("Concept");
73        assert_eq!(r.name, "about_concept");
74        assert_eq!(r.src_label, "Note");
75        assert_eq!(r.dst_label, "Concept");
76        assert_eq!(r.edge_type, "ABOUT");
77        assert_eq!(r.max_edges, Some(default_max_edges(&r.predicate)));
78    }
79
80    #[test]
81    fn concept_sources_rule_is_concept_to_file() {
82        let r = concept_sources_rule();
83        assert_eq!(r.name, "concept_sources");
84        assert_eq!(r.src_label, "Concept");
85        assert_eq!(r.dst_label, "File");
86        assert_eq!(r.edge_type, "DESCRIBED_IN");
87    }
88}