about summary refs log tree commit diff
path: root/src/tree.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tree.rs')
-rw-r--r--src/tree.rs91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/tree.rs b/src/tree.rs
new file mode 100644
index 0000000..f569dba
--- /dev/null
+++ b/src/tree.rs
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
+// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+use std::collections::BTreeSet;
+use std::ffi::{OsStr, OsString};
+
+use askama::Template;
+
+use crate::branches::next_branches;
+use crate::github;
+use crate::nixpkgs::Nixpkgs;
+
+#[derive(Debug, Template)]
+#[template(path = "tree.html")]
+pub struct Tree {
+    branch_name: String,
+    accepted: Option<bool>,
+    children: Vec<Tree>,
+}
+
+impl Tree {
+    fn generate(branch: String, found_branches: &mut BTreeSet<OsString>) -> Tree {
+        found_branches.insert((&branch).into());
+
+        let nexts = next_branches(&branch)
+            .into_iter()
+            .map(|b| Self::generate(b.to_string(), found_branches))
+            .collect();
+
+        Tree {
+            accepted: None,
+            branch_name: branch,
+            children: nexts,
+        }
+    }
+
+    fn fill_accepted(&mut self, branches: &BTreeSet<OsString>, missing_means_absent: bool) {
+        self.accepted = match branches.contains(OsStr::new(&self.branch_name)) {
+            true => Some(true),
+            false if missing_means_absent => Some(false),
+            false => None,
+        };
+
+        for child in self.children.iter_mut() {
+            child.fill_accepted(branches, missing_means_absent);
+        }
+    }
+
+    pub async fn make(base_branch: String, merge_status: &github::PullRequestStatus, nixpkgs: &Nixpkgs<'_>) -> Tree {
+        let mut missing_means_absent = true;
+        let mut branches = BTreeSet::new();
+
+        let mut tree = Self::generate(base_branch.clone(), &mut branches);
+
+        if let github::PullRequestStatus::Merged {
+            merge_commit_oid, ..
+        } = merge_status
+        {
+            if let Some(merge_commit) = merge_commit_oid {
+                let mut containing_commits = BTreeSet::new();
+
+                if let Err(e) =
+                    nixpkgs.branches_containing_commit(&merge_commit, &mut containing_commits)
+                        .await
+                {
+                    eprintln!("pr-tracker: branches_containing_commit: {}", e);
+                    missing_means_absent = false;
+                }
+
+                branches = branches
+                    .intersection(&containing_commits)
+                    .cloned()
+                    .collect();
+            } else {
+                branches.clear();
+                missing_means_absent = false;
+            }
+
+            // Even if something goes wrong with our local Git repo,
+            // or GitHub didn't tell us the merge commit, we know that
+            // the base branch of the PR must contain the commit,
+            // because GitHub told us it was merged into it.
+            branches.insert(base_branch.into());
+        } else {
+            branches.clear();
+        }
+
+        tree.fill_accepted(&branches, missing_means_absent);
+        tree
+    }
+}