Skip to main content

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}