cargo_coverage_gate/
error.rs1use serde_json::Value;
18
19#[ohno::error]
30#[from(
31 LoadMetadataError,
32 InvalidThresholdValueError,
33 ThresholdOutOfRangeError,
34 InvalidNoCoverableLinesValueError,
35 ConflictingCoverageMetadataError,
36 WorkspaceScopedNoCoverableLinesError,
37 ParseLcovError,
38 ReadLcovError,
39 UnknownPackageSelectorError
40)]
41pub struct CoverageGateError;
42
43#[ohno::error]
45#[display("failed to load workspace metadata")]
46#[from(cargo_metadata::Error)]
47pub(crate) struct LoadMetadataError;
48
49#[ohno::error]
52#[display("{source}: `coverage-gate.min-lines-percent` must be a number, got {min}")]
53pub(crate) struct InvalidThresholdValueError {
54 pub source: String,
55 pub min: Value,
56}
57
58#[ohno::error]
61#[display(
62 "invalid coverage-gate min-lines-percent value `{value}` for {source}: \
63 expected a value in {lower:.1}..={upper:.1}"
64)]
65pub(crate) struct ThresholdOutOfRangeError {
66 pub source: String,
67 pub value: f64,
68 pub lower: f64,
69 pub upper: f64,
70}
71
72#[ohno::error]
75#[display("{source}: `coverage-gate.expect-no-coverable-lines` must be a boolean, got {value}")]
76pub(crate) struct InvalidNoCoverableLinesValueError {
77 pub source: String,
78 pub value: Value,
79}
80
81#[ohno::error]
87#[display(
88 "{source}: `coverage-gate` cannot set both `min-lines-percent` and \
89 `expect-no-coverable-lines`; pick one"
90)]
91pub(crate) struct ConflictingCoverageMetadataError {
92 pub source: String,
93}
94
95#[ohno::error]
99#[display(
100 "`coverage-gate.expect-no-coverable-lines` is a package-level assertion and \
101 cannot be set in `[workspace.metadata.coverage-gate]`"
102)]
103pub(crate) struct WorkspaceScopedNoCoverableLinesError;
104
105#[ohno::error]
107#[display("lcov tracefile is not well-formed")]
108#[from(lcov::report::ParseError)]
109pub(crate) struct ParseLcovError;
110
111#[ohno::error]
114#[display("failed to read lcov tracefile `{path}`")]
115pub(crate) struct ReadLcovError {
116 pub path: String,
117}
118
119#[ohno::error]
121#[display("`--package` selector `{selector}` did not match any workspace member")]
122pub(crate) struct UnknownPackageSelectorError {
123 pub selector: String,
124}
125
126#[cfg(test)]
127#[cfg_attr(coverage_nightly, coverage(off))]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn umbrella_propagates_load_metadata_chain() {
133 let inner = LoadMetadataError::caused_by(std::io::Error::other("no manifest"));
134 let outer: CoverageGateError = inner.into();
135 let rendered = outer.to_string();
136 assert!(rendered.contains("failed to load workspace metadata"));
137 assert!(rendered.contains("no manifest"));
138 }
139
140 #[test]
141 fn umbrella_propagates_parse_lcov() {
142 let inner = ParseLcovError::new();
143 let outer: CoverageGateError = inner.into();
144 let rendered = outer.to_string();
145 assert!(rendered.contains("lcov tracefile"));
146 }
147
148 #[test]
149 fn unknown_package_selector_carries_pattern() {
150 let err = UnknownPackageSelectorError::new("nope-*".to_owned());
151 let rendered = err.to_string();
152 assert!(rendered.contains("nope-*"));
153 assert!(rendered.contains("did not match"));
154 }
155
156 #[test]
157 fn threshold_out_of_range_renders_value_and_bounds() {
158 let err = ThresholdOutOfRangeError::new("alpha".to_owned(), 150.0, 0.0, 100.0);
159 let rendered = err.to_string();
160 assert!(rendered.contains("150"));
161 assert!(rendered.contains("alpha"));
162 assert!(rendered.contains("0.0..=100.0"));
163 }
164
165 #[test]
166 fn invalid_no_coverable_lines_value_renders_source_and_value() {
167 let err = InvalidNoCoverableLinesValueError::new("alpha".to_owned(), Value::from("yes"));
168 let rendered = err.to_string();
169 assert!(rendered.contains("alpha"));
170 assert!(rendered.contains("expect-no-coverable-lines"));
171 assert!(rendered.contains("boolean"));
172 assert!(rendered.contains("yes"));
173 }
174
175 #[test]
176 fn conflicting_coverage_metadata_renders_source() {
177 let err = ConflictingCoverageMetadataError::new("alpha".to_owned());
178 let rendered = err.to_string();
179 assert!(rendered.contains("alpha"));
180 assert!(rendered.contains("min-lines-percent"));
181 assert!(rendered.contains("expect-no-coverable-lines"));
182 }
183
184 #[test]
185 fn workspace_scoped_no_coverable_lines_mentions_workspace() {
186 let err = WorkspaceScopedNoCoverableLinesError::new();
187 let rendered = err.to_string();
188 assert!(rendered.contains("expect-no-coverable-lines"));
189 assert!(rendered.contains("workspace.metadata.coverage-gate"));
190 }
191}