Skip to main content

callisto_model/
lib.rs

1//! Shared types and traits for callisto: package identity, versions, manifests, dependency
2//! specs, and the versioned JSON report contract.
3
4pub mod atomic;
5
6pub mod permit;
7pub use permit::*;
8
9pub mod tag;
10pub use tag::*;
11
12pub mod error;
13pub use error::*;
14
15pub mod path;
16pub use path::*;
17
18pub mod identity;
19pub use identity::*;
20
21pub mod ecosystem;
22pub use ecosystem::*;
23
24pub mod version;
25pub use version::*;
26
27pub mod severity;
28pub use severity::*;
29
30pub mod package;
31pub use package::*;
32
33pub mod dependency;
34pub use dependency::*;
35
36pub mod discovery;
37pub use discovery::*;
38
39pub mod exec;
40pub use exec::*;
41
42pub mod commit;
43pub use commit::*;
44
45pub mod diagnostic;
46pub use diagnostic::*;
47
48pub mod plan;
49pub use plan::*;
50
51pub mod report;
52pub use report::*;
53
54pub mod matrix;
55pub use matrix::*;
56
57pub mod registry;
58pub use registry::*;
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    fn assert_send_sync_static<T: Send + Sync + 'static>() {}
65
66    #[test]
67    fn test_auto_traits() {
68        assert_send_sync_static::<PackageId>();
69        assert_send_sync_static::<Version>();
70        assert_send_sync_static::<Severity>();
71        assert_send_sync_static::<Ecosystem>();
72        assert_send_sync_static::<Package>();
73        assert_send_sync_static::<PublishPlan>();
74        assert_send_sync_static::<PublishReport>();
75        assert_send_sync_static::<VersionReport>();
76        assert_send_sync_static::<StatusReport>();
77    }
78
79    /// Every source file across the workspace declaring at least one
80    /// `#[diagnostic(code(E..))]`, embedded at compile time via
81    /// `include_str!` (paths relative to this file). This list is the
82    /// actual audit surface -- a file added here needs no further wiring,
83    /// since the scan below extracts every code occurrence directly from
84    /// the text.
85    ///
86    /// A prior version hand-maintained a `&[&str]` list of expected codes
87    /// instead -- missed two entire files and a third of
88    /// `callisto-graph/src/error.rs`'s codes, undetected until a duplicate
89    /// (`E117` on two unrelated variants) surfaced by unrelated code
90    /// review. Scanning real source text at compile time removes that
91    /// maintenance step: a new file just needs adding here, and every
92    /// code it ever gains is covered from then on.
93    const DIAGNOSTIC_CODE_SOURCE_FILES: &[(&str, &str)] = &[
94        ("callisto-model/src/error.rs", include_str!("error.rs")),
95        ("callisto-model/src/exec.rs", include_str!("exec.rs")),
96        ("callisto-model/src/commit.rs", include_str!("commit.rs")),
97        ("callisto-model/src/version.rs", include_str!("version.rs")),
98        (
99            "callisto-graph/src/locate/mod.rs",
100            include_str!("../../callisto-graph/src/locate/mod.rs"),
101        ),
102        (
103            "callisto-graph/src/error.rs",
104            include_str!("../../callisto-graph/src/error.rs"),
105        ),
106        (
107            "callisto-format/src/bump.rs",
108            include_str!("../../callisto-format/src/bump.rs"),
109        ),
110        (
111            "callisto-format/src/changeset/mod.rs",
112            include_str!("../../callisto-format/src/changeset/mod.rs"),
113        ),
114        ("callisto-vcs/src/lib.rs", include_str!("../../callisto-vcs/src/lib.rs")),
115        (
116            "callisto-changelog/src/error.rs",
117            include_str!("../../callisto-changelog/src/error.rs"),
118        ),
119    ];
120
121    /// Extracts every `code(E<digits>)` occurrence from `text`, in order of appearance. Deliberately
122    /// a hand-rolled scan rather than a `regex` dependency — the pattern is fixed-shape and simple
123    /// enough that adding a whole crate dependency for it isn't warranted.
124    fn extract_diagnostic_codes(text: &str) -> Vec<String> {
125        let mut codes = Vec::new();
126        let mut rest = text;
127        while let Some(start) = rest.find("code(E") {
128            let after_marker = &rest[start + "code(".len()..];
129            let digit_end = after_marker
130                .find(|c: char| !c.is_ascii_digit() && c != 'E')
131                .unwrap_or(after_marker.len());
132            let candidate = &after_marker[..digit_end];
133            // `code(` is also used for things like `code(callisto::foo)` in test-double
134            // diagnostics elsewhere in the workspace (out of scope for this scan, since those
135            // files aren't in `DIAGNOSTIC_CODE_SOURCE_FILES`) -- guard here anyway so a stray
136            // non-numeric match can't silently produce a bogus "code".
137            if candidate.len() > 1 && candidate[1..].chars().all(|c| c.is_ascii_digit()) {
138                codes.push(candidate.to_string());
139            }
140            rest = &after_marker[digit_end..];
141        }
142        codes
143    }
144
145    /// Asserts that every `#[diagnostic(code(...))]` value across every file in
146    /// [`DIAGNOSTIC_CODE_SOURCE_FILES`] is unique workspace-wide. Duplicate diagnostic codes are
147    /// a real user-facing bug (E-codes are meant to be a stable, searchable identifier for one
148    /// specific error condition) — see this test's doc comment on `DIAGNOSTIC_CODE_SOURCE_FILES`
149    /// for the collision this replaced a broken, silently-incomplete version of the check.
150    #[test]
151    fn test_all_diagnostic_codes_are_unique() {
152        let mut seen: std::collections::BTreeMap<String, &str> = std::collections::BTreeMap::new();
153        for (file, text) in DIAGNOSTIC_CODE_SOURCE_FILES {
154            for code in extract_diagnostic_codes(text) {
155                if let Some(first_file) = seen.insert(code.clone(), file) {
156                    panic!("Duplicate diagnostic code {code}: declared in both {first_file} and {file}");
157                }
158            }
159        }
160        assert!(
161            seen.len() > 50,
162            "expected at least 50 distinct diagnostic codes across the workspace (81 at the \
163             time this test was written), got {} -- extract_diagnostic_codes likely stopped \
164             matching real source (check DIAGNOSTIC_CODE_SOURCE_FILES's include_str! paths \
165             still resolve)",
166            seen.len()
167        );
168    }
169
170    #[test]
171    fn extract_diagnostic_codes_finds_every_code_in_a_small_fixture() {
172        let text = r#"
173            #[diagnostic(code(E001))]
174            struct Foo;
175            #[diagnostic(
176                code(E042),
177                help("do something")
178            )]
179            struct Bar;
180        "#;
181        assert_eq!(extract_diagnostic_codes(text), vec!["E001", "E042"]);
182    }
183
184    #[test]
185    fn extract_diagnostic_codes_detects_a_duplicate_within_one_string() {
186        let text = "code(E001) ... code(E001)";
187        let codes = extract_diagnostic_codes(text);
188        let mut seen = std::collections::BTreeSet::new();
189        assert!(!codes.iter().all(|c| seen.insert(c.clone())));
190    }
191}