majordomus_cli/quality/mod.rs
1//! What this executable's own public surface is held to, measured by the executable.
2//!
3//! # What it is for
4//!
5//! Every other subsystem here is a projection of the capability registry, and the registry
6//! is what makes an operation impossible to declare twice. Nothing played that role for
7//! the crate's Rust surface: documentation, examples and behavioural coverage were
8//! properties a reviewer noticed or did not. This subsystem makes them a measurement with
9//! a stable code, a rule, a location and a remedy, so that the same finding reaches a
10//! terminal, a pipeline, an HTTP client and an agent from one reading.
11//!
12//! # The parts
13//!
14//! ```text
15//! source the crate as a syntax tree: what is exported, where, with what documentation
16//! policy what an item owes, and what counts as having paid it
17//! parity the registry against MCP, HTTP, OpenAPI and the command line
18//! report one measurement: counts and findings, sorted, deterministic
19//! model the vocabulary: codes, severities, the report's own shape
20//! rustdoc the tree rustdoc renders from the crate, judged against the same inventory
21//! leaks the shapes a published artifact must never carry, from the one file naming them
22//! ```
23//!
24//! Discovery happens once and every renderer reads the value: the same relationship the
25//! rest of the executable has with the registry, for the same reason.
26//!
27//! # Invariants
28//!
29//! * **Deterministic.** File reads are the only I/O; no network, no clock, no environment.
30//! Two runs over one tree produce equal reports, which is what lets a report be compared
31//! and committed.
32//! * **Whole or nothing.** A source file that cannot be parsed is an error, never a skip:
33//! a partial inventory that looked clean is the one result this must not produce.
34//! * **No exemption list.** What the policy does not ask of an item is derived from the
35//! item's kind and shape, and is reported as an exemption with its reason, so that what
36//! is not demanded is as visible as what is.
37//!
38//! # Errors
39//!
40//! Everything here returns [`crate::Error::InvalidSource`] and nothing else: a directory
41//! that is not a crate, a file that cannot be read, a file that does not parse, or a
42//! `mod` declaration that resolves to no file.
43//!
44//! ```
45//! use majordomus_cli::quality;
46//!
47//! // the whole lifecycle: build a crate, measure it, read the verdict
48//! let dir = tempfile::tempdir().unwrap();
49//! let src = dir.path().join("src");
50//! std::fs::create_dir_all(&src).unwrap();
51//! std::fs::write(src.join("lib.rs"), "//! Thin.\npub fn go() {}\n").unwrap();
52//!
53//! let report = quality::report::rust_only(dir.path(), "fixture").unwrap();
54//! assert!(!report.passes());
55//! assert_eq!(report.exit_code(), 10);
56//!
57//! // and every finding names the rule that requires it and what to do about it
58//! let first = &report.violations[0];
59//! assert!(first.rule.starts_with("project."));
60//! assert!(!first.remediation.is_empty());
61//! ```
62
63pub(crate) mod leaks;
64pub(crate) mod model;
65pub mod parity;
66pub mod policy;
67pub mod report;
68// crate-visible only: its report reaches every consumer as the typed output of the
69// capability `quality.rustdoc`, and nothing outside the crate needs its Rust items
70pub(crate) mod rustdoc;
71pub mod source;
72
73pub use model::{
74 Exemption, ModuleQuality, OperationParity, PublicApiQuality, QualityReport, Severity,
75 Violation, ViolationCode, SCHEMA,
76};
77pub use policy::{ExampleRequirement, Verdict};
78pub use source::{Example, Inventory, Item, ItemKind};
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn the_subsystem_measures_its_own_crate_and_answers_about_it() {
86 // the dogfooding assertion: this runs against the crate it lives in, which is the
87 // only way the numbers in the report are ever about something real
88 let report = report::rust_only(std::path::Path::new("."), "apps/majordomus-cli").unwrap();
89 assert!(report.public_api.items > 100, "the crate exports a surface");
90 assert_eq!(
91 report.public_api.items, report.public_api.documented,
92 "missing_docs is denied at compile time, so every exported item is documented"
93 );
94 assert_eq!(report.modules.modules, report.modules.documented);
95 assert!(report
96 .violations
97 .iter()
98 .all(|v| v.severity == Severity::Error));
99 }
100
101 #[test]
102 fn a_directory_that_is_not_a_crate_is_an_error_and_never_an_empty_pass() {
103 let dir = tempfile::tempdir().unwrap();
104 let err = report::rust_only(dir.path(), "nothing").unwrap_err();
105 assert!(matches!(err, crate::Error::InvalidSource { .. }));
106 assert_eq!(err.exit_code(), 10);
107 }
108}