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, Checksum, GenId, Generation, Reason, Resolution, Scope, StoreKey, ToolName,
25};
26pub use version::{Request, VersionReq};
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn error_code_format() {
34        let e = VtaError::new(Area::Cfg, 7, "bad value");
35        assert_eq!(e.code(), "VTA-CFG-0007");
36        assert_eq!(e.exit(), ExitCode::Config);
37        assert_eq!(format!("{e}"), "error[VTA-CFG-0007]: bad value");
38    }
39
40    #[test]
41    fn platform_token_roundtrip() {
42        let p = Platform::parse("linux/x86_64/gnu").unwrap();
43        assert_eq!(p.token(), "linux/x86_64/gnu");
44        let m = Platform::parse("macos/aarch64").unwrap();
45        assert_eq!(m.token(), "macos/aarch64");
46        assert!(Platform::parse("plan9/sparc").is_err());
47    }
48
49    #[test]
50    fn request_parsing() {
51        let r = Request::parse("node@24").unwrap();
52        assert_eq!(r.tool, "node");
53        assert_eq!(r.version, VersionReq::Prefix("24".into()));
54        assert_eq!(Request::parse("rust").unwrap().version, VersionReq::Latest);
55        assert_eq!(
56            Request::parse("python@3.13.4").unwrap().version,
57            VersionReq::Exact("3.13.4".into())
58        );
59        assert_eq!(
60            Request::parse("go@latest").unwrap().version,
61            VersionReq::Latest
62        );
63        assert_eq!(
64            Request::parse("node@^24").unwrap().version,
65            VersionReq::Range("^24".into())
66        );
67    }
68
69    #[test]
70    fn store_key_validation() {
71        let hex = "a".repeat(64);
72        assert!(StoreKey::new(format!("blake3-{hex}")).is_ok());
73        assert!(StoreKey::new("sha1-deadbeef").is_err());
74        // M7: suffix must be exactly 64 lowercase hex chars — reject traversal,
75        // wrong length, and uppercase.
76        assert!(StoreKey::new("blake3-../../etc").is_err());
77        assert!(StoreKey::new("blake3-aa3f").is_err()); // too short
78        assert!(StoreKey::new(format!("blake3-{}", "A".repeat(64))).is_err()); // uppercase
79        assert!(StoreKey::new(format!("blake3-{}", "a".repeat(63))).is_err());
80    }
81
82    #[test]
83    fn gen_id_display() {
84        assert_eq!(GenId(8).to_string(), "0008");
85        assert_eq!(GenId(1234).to_string(), "1234");
86    }
87}