weavatrix_rust/report/mod.rs
1//! Composes repository evidence into a report a person can read and share.
2//!
3//! The composer returns bytes and writes nothing. Publishing a document is an
4//! explicit act by a person at a command line, not a side effect an agent can
5//! trigger through a query, so the read-only operation surface stays read-only
6//! and the only component that touches the filesystem for output is the CLI.
7
8mod html;
9mod layout;
10mod markdown;
11mod sections;
12mod tables;
13
14use crate::engine::Weavatrix;
15use blazingly_json::{Value, json};
16
17/// The engine identity a report states about itself. Read from the package
18/// rather than through the public facade, which depends on this module.
19const ENGINE: &str = env!("CARGO_PKG_VERSION");
20
21/// One composed report in its three renderings.
22pub struct Report {
23 /// The Markdown document.
24 pub markdown: String,
25 /// The self-contained HTML page, including its module map.
26 pub html: String,
27 /// The gathered evidence, for a consumer that renders its own view.
28 pub data: Value,
29}
30
31/// Composes a report from the repository the engine currently targets.
32///
33/// Every section is an ordinary bounded operation from the read-only catalog.
34/// A capability this build did not compile is recorded as absent with its
35/// reason rather than rendered as an empty section.
36///
37/// # Errors
38///
39/// Returns the first operation failure. A missing optional capability is not
40/// a failure; a repository that cannot be analyzed is.
41pub fn compose(engine: &mut Weavatrix) -> Result<Report, String> {
42 let evidence = sections::gather(engine)?;
43 let summary = tables::summary(&evidence);
44 let sections = tables::sections(&evidence);
45 Ok(Report {
46 markdown: markdown::render(&summary, §ions, evidence.depth, ENGINE),
47 html: html::render(&summary, §ions, &evidence, ENGINE),
48 data: data(&evidence),
49 })
50}
51
52fn data(evidence: §ions::Evidence) -> Value {
53 json!({
54 "schema": "weavatrix.report.v1",
55 "engine": ENGINE,
56 "graph_stats": evidence.stats,
57 "verify_architecture": evidence.architecture,
58 "god_nodes": evidence.hubs,
59 "hot_path_review": evidence.hot,
60 "find_dead_code": evidence.dead,
61 "find_duplicates": evidence.duplicates,
62 "list_endpoints": evidence.endpoints,
63 "module_map": {
64 "depth": evidence.depth,
65 "modules": evidence.modules.iter().map(|module| json!({
66 "path": module.path,
67 "files": module.files,
68 "symbols": module.symbols,
69 "violations": module.violations,
70 "x": module.position.0,
71 "y": module.position.1
72 })).collect::<Vec<_>>(),
73 "links": evidence.links.iter().map(|(from, to, weight)| json!({
74 "from": from,
75 "to": to,
76 "weight": weight
77 })).collect::<Vec<_>>()
78 }
79 })
80}