Skip to main content

aprender_contracts_cli/commands/
validate.rs

1use std::path::Path;
2
3use provable_contracts::error::Severity;
4use provable_contracts::schema::{validate_artifact, ArtifactKind};
5
6/// `pv validate <path>` — validate whatever artifact `path` holds.
7///
8/// Dispatches on what the file IS, not on the assumption that everything under
9/// `contracts/` is a `Contract`. Five files in the corpus are not: two pv
10/// binding registries and three publish manifests, all of which failed here
11/// with ``missing field `metadata` `` while the directory walkers that lint
12/// them already knew to treat them differently. See
13/// `provable_contracts::schema::artifact`.
14pub fn run(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
15    let (kind, violations) = validate_artifact(path)?;
16
17    let errors: Vec<_> = violations
18        .iter()
19        .filter(|v| v.severity == Severity::Error)
20        .collect();
21    let warnings: Vec<_> = violations
22        .iter()
23        .filter(|v| v.severity == Severity::Warning)
24        .collect();
25
26    for v in &violations {
27        println!("{v}");
28    }
29
30    println!("\n{} error(s), {} warning(s)", errors.len(), warnings.len());
31
32    if errors.is_empty() {
33        println!("{} is valid.", noun(kind));
34        Ok(())
35    } else {
36        Err(format!("{} has {} validation error(s)", noun(kind), errors.len()).into())
37    }
38}
39
40/// How to name the artifact in the verdict line. Naming the kind is the point:
41/// a reader who runs `pv validate contracts/binding.yaml` and is told
42/// "Contract is valid." has been told something false about which rules ran.
43fn noun(kind: ArtifactKind) -> &'static str {
44    match kind {
45        ArtifactKind::Contract => "Contract",
46        ArtifactKind::Binding => "Binding registry (kind: binding)",
47        ArtifactKind::PublishManifest => "Publish manifest (kind: publish-manifest)",
48    }
49}