Skip to main content

comindly_core/
view.rs

1//! What the apps show of an index — graph, contracts, ledger, conventions, dependencies — read once.
2
3use crate::conventions::Convention;
4use crate::graph::{CodeGraph, ContractView};
5use crate::ledger::Entry;
6use crate::manifests::InternalDep;
7use crate::model::{Edge, Symbol};
8
9pub use comindly_api::view::View;
10
11/// Everything the UI shows, loaded from an index.
12pub struct Data {
13    pub graph: CodeGraph,
14    pub symbols: Vec<Symbol>,
15    pub edges: Vec<Edge>,
16    pub contracts: Vec<ContractView>,
17    pub entries: Vec<Entry>,
18    /// Measured conventions; `manual` holds the team's changes to them.
19    pub conventions: Vec<Convention>,
20    pub manual: Vec<crate::conventions::Manual>,
21    pub deps: Vec<InternalDep>,
22    /// Index root for writing ledger reviews; `None` makes the UI read-only.
23    pub root: Option<String>,
24    /// Why the index could not be loaded (the UIs then start empty).
25    pub problem: Option<String>,
26}
27
28impl Data {
29    /// Everything the UIs show, read from the index at `root`.
30    pub fn load(root: &str, verifier: &crate::signing::Verifier) -> anyhow::Result<Data> {
31        let dir = format!("{}/_graph", root.trim_end_matches('/'));
32        let (symbols, edges) = crate::index::read_graph_blocking(&dir)
33            .map_err(|e| anyhow::anyhow!("no index at {dir}: {e:#}"))?;
34        let mut conventions = match crate::index::read_facts_blocking(&dir) {
35            Ok(Some(facts)) => crate::conventions::mine(&symbols, &edges, &facts),
36            _ => Vec::new(),
37        };
38        conventions.extend(crate::index::read_recipes_blocking(&dir).unwrap_or_default());
39        let manual = crate::conventions::manual_blocking(root).unwrap_or_default();
40        let records = crate::ledger::all_blocking(root).unwrap_or_default();
41        let mut entries = crate::ledger::derive(&records, &symbols, verifier);
42        let packages = crate::index::read_packages_blocking(&dir).unwrap_or_default();
43        let findings = crate::checks::run(&entries, &symbols, &edges, &packages);
44        crate::checks::promote(&mut entries, &findings);
45        let deps = crate::manifests::internal(&packages);
46        let mut data = Data::from_view(View {
47            symbols: symbols.into(),
48            edges: edges.into(),
49            entries: entries.into(),
50            conventions: conventions.into(),
51            manual: manual.into(),
52            deps: deps.into(),
53            problem: None,
54        });
55        data.root = Some(root.to_string());
56        Ok(data)
57    }
58
59    /// The UI's data from a transferred view; the graph and contracts are rebuilt. Read-only.
60    pub fn from_view(v: View<'_>) -> Data {
61        let (symbols, edges) = (v.symbols.into_owned(), v.edges.into_owned());
62        let graph = CodeGraph::build(&symbols, &edges);
63        Data {
64            contracts: graph.contracts(None, false),
65            graph,
66            symbols,
67            edges,
68            entries: v.entries.into_owned(),
69            conventions: v.conventions.into_owned(),
70            manual: v.manual.into_owned(),
71            deps: v.deps.into_owned(),
72            root: None,
73            problem: v.problem,
74        }
75    }
76
77    /// No index yet: an empty view of `root` that says why.
78    pub fn empty(root: &str, problem: String) -> Data {
79        Data {
80            graph: CodeGraph::build(&[], &[]),
81            symbols: Vec::new(),
82            edges: Vec::new(),
83            contracts: Vec::new(),
84            entries: Vec::new(),
85            conventions: Vec::new(),
86            manual: Vec::new(),
87            deps: Vec::new(),
88            root: Some(root.to_string()),
89            problem: Some(problem),
90        }
91    }
92
93    /// The index at `root`, or an empty view saying why it could not be read.
94    pub fn load_or_empty(root: &str, verifier: &crate::signing::Verifier) -> Data {
95        Data::load(root, verifier).unwrap_or_else(|e| Data::empty(root, format!("{e:#}")))
96    }
97}