Compute the triage candidate set for `feature` relative to `target`. See the module documentation for the semantics of each field. # Errors Returns [`RepositoryError::ViewNotFound`] if either view does not exist, or [`RepositoryError::Database`] on any pristine access failure.
(
&self,
feature: &str,
target: &str,
)
| 77 | /// Returns [`RepositoryError::ViewNotFound`] if either view does not exist, |
| 78 | /// or [`RepositoryError::Database`] on any pristine access failure. |
| 79 | pub fn triage_candidate_set( |
| 80 | &self, |
| 81 | feature: &str, |
| 82 | target: &str, |
| 83 | ) -> Result<CandidateSet, RepositoryError> { |
| 84 | // Step 1: the raw change-level diff. `.0` is only-in-feature. |
| 85 | let (only_in_feature_hashes, _only_in_target, _common) = |
| 86 | self.diff_views(feature, target)?; |
| 87 | |
| 88 | let txn = self |
| 89 | .pristine |
| 90 | .read_txn() |
| 91 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 92 | |
| 93 | // The target view's full visible set (with dependency closure). Any |
| 94 | // closure addition already visible to the target is not an "addition". |
| 95 | let target_view = txn |
| 96 | .get_view(target) |
| 97 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 98 | .ok_or_else(|| RepositoryError::ViewNotFound { |
| 99 | name: target.to_string(), |
| 100 | })?; |
| 101 | let target_visible = collect_visible_change_ids_with_deps(&txn, &target_view)?; |
| 102 | |
| 103 | // Seed the closure with the only-in-feature node ids, remembering the |
| 104 | // seed set so we can subtract it from the additions later. |
| 105 | let mut feature_node_ids: HashSet<NodeId> = HashSet::new(); |
| 106 | for hash in &only_in_feature_hashes { |
| 107 | if let Some(id) = txn |
| 108 | .get_internal(hash) |
| 109 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 110 | { |
| 111 | feature_node_ids.insert(id); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // Step 2: expand the transitive dependency closure in place. |
| 116 | let mut closure: HashSet<NodeId> = feature_node_ids.clone(); |
| 117 | expand_indexed_dependency_closure(&txn, &mut closure)?; |
| 118 | |
| 119 | // Closure additions = closure minus the seed minus the target's set. |
| 120 | let mut addition_hashes: Vec<Hash> = Vec::new(); |
| 121 | for id in &closure { |
| 122 | if feature_node_ids.contains(id) || target_visible.contains(id) { |
| 123 | continue; |
| 124 | } |
| 125 | if let Some(hash) = txn |
| 126 | .get_external(*id) |
| 127 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 128 | { |
| 129 | addition_hashes.push(hash); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | // Deterministic ordering. |
| 134 | let mut only_in_feature: Vec<String> = only_in_feature_hashes |
| 135 | .iter() |
| 136 | .map(|h| h.to_base32()) |