devscout_rs/semantic/
uncertainty.rs1#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum Uncertainty {
12 Confirmed,
14 Candidate,
18 Ambiguous,
20 Unresolved,
22 Unsupported,
26 Excluded,
30 Stale {
33 reason: String,
36 },
37 Incomplete {
41 reason: String,
43 },
44}
45
46impl Uncertainty {
47 pub fn reason(&self) -> String {
50 match self {
51 Uncertainty::Confirmed => "confirmed".to_string(),
52 Uncertainty::Candidate => "unrecognised-resolution".to_string(),
53 Uncertainty::Ambiguous => "compiler-ambiguous".to_string(),
54 Uncertainty::Unresolved => "compiler-unresolved".to_string(),
55 Uncertainty::Unsupported => "dynamic-dispatch".to_string(),
56 Uncertainty::Excluded => "inaccessible".to_string(),
57 Uncertainty::Stale { reason } => reason.clone(),
58 Uncertainty::Incomplete { reason } => reason.clone(),
59 }
60 }
61
62 pub fn is_confirmed(&self) -> bool {
66 matches!(self, Uncertainty::Confirmed)
67 }
68}
69
70pub fn from_resolution(resolution: &str) -> Uncertainty {
76 match resolution {
77 "confirmed" => Uncertainty::Confirmed,
78 "ambiguous" => Uncertainty::Ambiguous,
79 "unresolved" => Uncertainty::Unresolved,
80 "dynamic" => Uncertainty::Unsupported,
81 "inaccessible" => Uncertainty::Excluded,
82 _ => Uncertainty::Candidate,
83 }
84}
85
86pub fn from_compilation_state(state: &str, reason: &str) -> Option<Uncertainty> {
92 match state {
93 "complete" => None,
94 "unsupported" => Some(Uncertainty::Unsupported),
95 _ => Some(Uncertainty::Incomplete {
96 reason: reason.to_string(),
97 }),
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn recognised_resolutions_map_to_their_own_state() {
107 assert_eq!(from_resolution("confirmed"), Uncertainty::Confirmed);
108 assert_eq!(from_resolution("ambiguous"), Uncertainty::Ambiguous);
109 assert_eq!(from_resolution("unresolved"), Uncertainty::Unresolved);
110 assert_eq!(from_resolution("dynamic"), Uncertainty::Unsupported);
111 assert_eq!(from_resolution("inaccessible"), Uncertainty::Excluded);
112 }
113
114 #[test]
115 fn an_unrecognised_resolution_is_a_candidate_not_a_confirmation() {
116 assert_eq!(from_resolution("future-outcome"), Uncertainty::Candidate);
117 assert!(!from_resolution("future-outcome").is_confirmed());
118 }
119
120 #[test]
121 fn only_confirmed_participates_in_override() {
122 assert!(Uncertainty::Confirmed.is_confirmed());
123 assert!(!Uncertainty::Ambiguous.is_confirmed());
124 assert!(!Uncertainty::Unresolved.is_confirmed());
125 assert!(!Uncertainty::Unsupported.is_confirmed());
126 assert!(!Uncertainty::Excluded.is_confirmed());
127 assert!(!Uncertainty::Candidate.is_confirmed());
128 assert!(!Uncertainty::Stale {
129 reason: "x".to_string()
130 }
131 .is_confirmed());
132 assert!(!Uncertainty::Incomplete {
133 reason: "x".to_string()
134 }
135 .is_confirmed());
136 }
137
138 #[test]
139 fn a_complete_compilation_defers_to_the_occurrence_resolution() {
140 assert_eq!(from_compilation_state("complete", "complete"), None);
141 }
142
143 #[test]
144 fn a_partial_or_unsupported_compilation_overrides_the_occurrence() {
145 assert_eq!(
146 from_compilation_state("partial", "binding-error"),
147 Some(Uncertainty::Incomplete {
148 reason: "binding-error".to_string()
149 })
150 );
151 assert_eq!(
152 from_compilation_state("unsupported", "no-target"),
153 Some(Uncertainty::Unsupported)
154 );
155 }
156
157 #[test]
158 fn every_state_carries_a_nonempty_reason() {
159 let states = [
160 Uncertainty::Confirmed,
161 Uncertainty::Candidate,
162 Uncertainty::Ambiguous,
163 Uncertainty::Unresolved,
164 Uncertainty::Unsupported,
165 Uncertainty::Excluded,
166 Uncertainty::Stale {
167 reason: "stale-x".to_string(),
168 },
169 Uncertainty::Incomplete {
170 reason: "incomplete-x".to_string(),
171 },
172 ];
173 for state in states {
174 assert!(!state.reason().is_empty());
175 }
176 }
177}