Skip to main content

vanta_core/
lib.rs

1//! `vanta-core` — the shared vocabulary every Vanta crate speaks.
2//!
3//! This crate defines the nouns of the system (see `docs/02-architecture.md`):
4//! [`Request`], [`Resolution`], [`Artifact`], [`StoreKey`], [`Generation`],
5//! [`Platform`], [`Scope`]; the trait seams that the engine is wired through
6//! ([`Provider`], [`Backend`], [`CacheStore`], [`SignatureVerifier`],
7//! [`LinkStrategy`]); and the [`VtaError`] taxonomy with stable `VTA-<AREA>-<NNNN>`
8//! codes and process [`ExitCode`]s (see `docs/25-error-and-exit-code-catalog.md`).
9//!
10//! It is the leaf of the crate dependency graph and has no third-party
11//! dependencies, so every other crate can depend on it freely.
12#![forbid(unsafe_code)]
13
14pub mod error;
15pub mod platform;
16pub mod traits;
17pub mod types;
18pub mod version;
19
20pub use error::{Area, ExitCode, VtaError, VtaResult};
21pub use platform::{Arch, Libc, Os, Platform};
22pub use traits::{Backend, CacheStore, LinkStrategy, Provider, SignatureVerifier};
23pub use types::{
24    Artifact, BuildRecipe, Checksum, GenId, Generation, Reason, Resolution, Scope, StoreKey,
25    ToolName,
26};
27pub use version::{Request, VersionReq};
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn error_code_format() {
35        let e = VtaError::new(Area::Cfg, 7, "bad value");
36        assert_eq!(e.code(), "VTA-CFG-0007");
37        assert_eq!(e.exit(), ExitCode::Config);
38        assert_eq!(format!("{e}"), "error[VTA-CFG-0007]: bad value");
39    }
40
41    #[test]
42    fn platform_token_roundtrip() {
43        let p = Platform::parse("linux/x86_64/gnu").unwrap();
44        assert_eq!(p.token(), "linux/x86_64/gnu");
45        let m = Platform::parse("macos/aarch64").unwrap();
46        assert_eq!(m.token(), "macos/aarch64");
47        assert!(Platform::parse("plan9/sparc").is_err());
48    }
49
50    #[test]
51    fn request_parsing() {
52        let r = Request::parse("node@24").unwrap();
53        assert_eq!(r.tool, "node");
54        assert_eq!(r.version, VersionReq::Prefix("24".into()));
55        assert_eq!(Request::parse("rust").unwrap().version, VersionReq::Latest);
56        assert_eq!(
57            Request::parse("python@3.13.4").unwrap().version,
58            VersionReq::Exact("3.13.4".into())
59        );
60        assert_eq!(
61            Request::parse("go@latest").unwrap().version,
62            VersionReq::Latest
63        );
64        assert_eq!(
65            Request::parse("node@^24").unwrap().version,
66            VersionReq::Range("^24".into())
67        );
68    }
69
70    #[test]
71    fn store_key_validation() {
72        let hex = "a".repeat(64);
73        assert!(StoreKey::new(format!("blake3-{hex}")).is_ok());
74        assert!(StoreKey::new("sha1-deadbeef").is_err());
75        // M7: suffix must be exactly 64 lowercase hex chars — reject traversal,
76        // wrong length, and uppercase.
77        assert!(StoreKey::new("blake3-../../etc").is_err());
78        assert!(StoreKey::new("blake3-aa3f").is_err()); // too short
79        assert!(StoreKey::new(format!("blake3-{}", "A".repeat(64))).is_err()); // uppercase
80        assert!(StoreKey::new(format!("blake3-{}", "a".repeat(63))).is_err());
81    }
82
83    #[test]
84    fn gen_id_display() {
85        assert_eq!(GenId(8).to_string(), "0008");
86        assert_eq!(GenId(1234).to_string(), "1234");
87    }
88}