aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ba/back/src/git_bug/dag
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ba/back/src/git_bug/dag')
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/dag/mod.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
new file mode 100644
index 0000000..9c158a7
--- /dev/null
+++ b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
@@ -0,0 +1,143 @@
+// Back - An extremely simple git issue tracking system. Inspired by tvix's
+// panettone
+//
+// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This file is part of Back.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/agpl.txt>.
+
+use std::path::Path;
+
+use gix::{bstr::ByteSlice, refs::Target, Commit, Id, ObjectId, Repository};
+
+use crate::error;
+
+use super::issue::{
+ entity::{Entity, RawEntity},
+ CollapsedIssue, RawCollapsedIssue,
+};
+
+#[derive(Debug)]
+pub struct Dag {
+ entities: Vec<Entity>,
+}
+
+impl Dag {
+ pub fn collapse(self) -> CollapsedIssue {
+ let raw_collapsed_issue = self.entities.into_iter().rev().fold(
+ RawCollapsedIssue::default(),
+ |mut collapsed_issue, entity| {
+ collapsed_issue.append_entity(entity);
+ collapsed_issue
+ },
+ );
+
+ CollapsedIssue::from(raw_collapsed_issue)
+ }
+
+ /// Construct a DAG from the root child upwards.
+ pub fn construct(repo: &Repository, id: ObjectId) -> Self {
+ let mut entities = vec![];
+
+ let base_commit = repo
+ .find_object(id)
+ .expect("The object with this id should exist.")
+ .try_into_commit()
+ .expect("The git-bug's data model enforces this.");
+
+ entities.push(Self::commit_to_operations(repo, &base_commit));
+
+ let mut current_commit = base_commit;
+ while let Some(parent_id) = Self::try_get_parent(repo, &current_commit) {
+ entities.push(Self::commit_to_operations(repo, &parent_id));
+ current_commit = parent_id;
+ }
+
+ Self {
+ entities: {
+ entities
+ .into_iter()
+ .map(|(raw_entity, id)| Entity::from_raw(repo, raw_entity, id))
+ .collect()
+ },
+ }
+ }
+
+ fn commit_to_operations<'b>(repo: &Repository, id: &Commit<'b>) -> (RawEntity, Id<'b>) {
+ let tree_obj = repo
+ .find_object(
+ id.tree_id()
+ .expect("All of git-bug's commits should have trees attached to them'"),
+ )
+ .expect("The object with this id should exist.")
+ .try_into_tree()
+ .expect("git-bug's data model enforces this.");
+
+ let ops_ref = tree_obj
+ .find_entry("ops")
+ .expect("All of git-bug's trees should contain a 'ops' json file");
+
+ let issue_data = repo
+ .find_object(ops_ref.object_id())
+ .expect("The object with this id should exist.")
+ .try_into_blob()
+ .expect("The git-bug's data model enforces this.")
+ .data
+ .clone();
+
+ let operations = serde_json::from_str(
+ issue_data
+ .to_str()
+ .expect("git-bug's ensures, that this is valid json."),
+ )
+ .expect("The returned json should be valid");
+
+ (operations, id.id())
+ }
+
+ fn try_get_parent<'a>(repo: &'a Repository, base_commit: &Commit<'a>) -> Option<Commit<'a>> {
+ let count = base_commit.parent_ids().count();
+
+ match count {
+ 0 => None,
+ 1 => {
+ let parent = base_commit.parent_ids().last().expect("One does exist");
+
+ let parent_id = parent.object().expect("The object exists").id;
+ Some(
+ repo.find_object(parent_id)
+ .expect("This is a valid id")
+ .try_into_commit()
+ .expect("This should be a commit"),
+ )
+ }
+ other => {
+ unreachable!(
+ "Each commit, used by git-bug should only have one parent, but found: {other}"
+ );
+ }
+ }
+ }
+}
+
+pub fn issues_from_repository(repo: &Repository) -> error::Result<Vec<Dag>> {
+ let dags = repo
+ .refs
+ .iter()?
+ .prefixed(Path::new("refs/bugs/"))?
+ .map(|val| {
+ let reference = val.expect("All `git-bug` references in 'refs/bugs' should be objects");
+
+ if let Target::Object(id) = reference.target {
+ Dag::construct(repo, id)
+ } else {
+ unreachable!("All 'refs/bugs/' should contain a clear target.");
+ }
+ })
+ .collect::<Vec<Dag>>();
+
+ Ok(dags)
+}