devscout_rs/query/outcome.rs
1// The closed `outcome` vocabulary every `--json` answer on `refs`/`read`/
2// `impact`/`tests` carries. Defined once, here, as a Rust enum rather than a
3// bag of string literals scattered across `cli.rs`'s JSON builders: an
4// `Outcome` value can only ever print one of the four words below, so no
5// caller can typo a fifth one into existence.
6
7/// The version of the `--json` answer shape every `refs`/`read`/`impact`/
8/// `tests` object carries as its first key. Bump it only when an existing key
9/// is renamed, removed, or given a different meaning -- adding a new key, as
10/// `schema_version` and `why` themselves were added, never requires a bump. A
11/// consumer should ignore any key it does not recognise.
12pub const SCHEMA_VERSION: u64 = 1;
13
14/// One of the four words a query verb's `--json` answer names its result
15/// with.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Outcome {
18 /// The seed resolved (a type, a member, or a file) and the answer is the
19 /// ordinary shape a caller reads.
20 Hit,
21 /// The seed resolved but the answer itself is empty: `impact` with no affected file,
22 /// or `refs`/`read` on a member declared by exactly one type with nothing referencing it.
23 ZeroHit,
24 /// The seed named more than one candidate (a type, or a member across
25 /// more than one declaring type) and nothing was guessed between them.
26 Ambiguous,
27 /// Nothing in the graph carries the seed at all; the caller's zero-hit
28 /// note advises a text-search fallback.
29 FallbackAdvised,
30}
31
32impl Outcome {
33 /// Every value, in the order the doc comment above lists them.
34 pub const ALL: [Outcome; 4] = [
35 Outcome::Hit,
36 Outcome::ZeroHit,
37 Outcome::Ambiguous,
38 Outcome::FallbackAdvised,
39 ];
40
41 /// The exact `--json` word for this outcome.
42 pub fn as_str(self) -> &'static str {
43 match self {
44 Outcome::Hit => "hit",
45 Outcome::ZeroHit => "zero-hit",
46 Outcome::Ambiguous => "ambiguous",
47 Outcome::FallbackAdvised => "fallback-advised",
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn every_outcome_word_is_one_of_the_closed_vocabulary() {
58 const EXPECTED: &[&str] = &["hit", "zero-hit", "ambiguous", "fallback-advised"];
59 let words: Vec<&str> = Outcome::ALL.iter().map(|o| o.as_str()).collect();
60 for word in &words {
61 assert!(
62 EXPECTED.contains(word),
63 "{word} is not in the closed outcome vocabulary"
64 );
65 }
66 assert_eq!(
67 words.len(),
68 EXPECTED.len(),
69 "ALL must name every vocabulary word exactly once"
70 );
71 }
72}