cove_rules_check/check.rs
1//! `cove check`, for a rule package written against this application's own
2//! host module.
3//!
4//! # Why this exists
5//!
6//! The person who writes rules is not the person who wrote the embedder. Their
7//! toolchain is `cove fmt`, `cove check` and `cove test`, and `cove check`
8//! stops at `use reviews`:
9//!
10//! ```text
11//! warning[cove::resolve::unchecked_host]: no Host API schema describes the
12//! host module `reviews`, so calls into it are unchecked
13//! ```
14//!
15//! which is accurate. `reviews` is this crate's, and no `cove` command has
16//! heard of it. Issue #151 asked for a flag or a `cove.toml` key that would
17//! let one; `cove_sema::compile`'s module doc is where the answer is argued,
18//! and the answer is no. A description in a config file is a second
19//! description of a module whose first one is Rust, and two descriptions of
20//! one thing is the drift ADR 0017 exists to prevent; and a schema would let
21//! `cove check` check a package that `cove test` still could not run, because
22//! what answers a call is an implementation and no format carries one.
23//!
24//! So the toolchain is the embedder's to provide, and this is the whole of it.
25//! It is not a fork of `cove check`: it reads the same package from disk, runs
26//! the same `cove_sema::Compiler`, and renders diagnostics with the same
27//! `cove_diag::render`, with one line's difference — the schemas it was handed.
28//!
29//! ```console
30//! $ cargo run -p cove-rules --bin cove-rules-check
31//! `rules` checks against `reviews`: 10 files, 6 modules, no notices
32//! ```
33//!
34//! # What it does not do
35//!
36//! It does not run the package's `test fn` declarations. That needs the
37//! `reviews` *implementation* as well as its description, which is the same
38//! argument arriving from the other end: an embedder that wants `cove test`
39//! registers [`cove_rules::Reviews`] beside the schema and runs the tests
40//! against it. Nothing in the way stops that; it is simply more than one line,
41//! and this file is the one-line half.
42
43use std::process::ExitCode;
44
45use cove_rules::{package_root, RulePackage, REVIEWS};
46
47fn main() -> ExitCode {
48 // The same value `Reviews::module_schema` answers with. That it is one
49 // value is the whole point: a checker reading a copy of the description
50 // the boundary enforces would be a checker that can be right about a
51 // module the run is not going to have.
52 let package = match RulePackage::load(&package_root(), REVIEWS) {
53 Ok(package) => package,
54 Err(report) => {
55 eprint!("{report}");
56 return ExitCode::FAILURE;
57 }
58 };
59
60 let notices = package.notices();
61 for notice in ¬ices {
62 eprint!("{notice}");
63 }
64
65 let cost = package.cost();
66 let count = match notices.len() {
67 0 => "no notices".to_string(),
68 1 => "1 notice".to_string(),
69 many => format!("{many} notices"),
70 };
71 println!(
72 "`rules` checks against `{}`: {} files, {} modules, {count}",
73 REVIEWS.name, cost.files, cost.modules
74 );
75 ExitCode::SUCCESS
76}