diff options
Diffstat (limited to 'pkgs/by-name/ba/back/src/git_bug/issue/mod.rs')
-rw-r--r-- | pkgs/by-name/ba/back/src/git_bug/issue/mod.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs new file mode 100644 index 0000000..f27bfec --- /dev/null +++ b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs @@ -0,0 +1,185 @@ +// 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::fmt::Display; + +use entity::{Entity, Id}; +use identity::Author; +use label::Label; +use operation::Operation; +use serde_json::Value; + +use super::format::{MarkDown, TimeStamp}; + +pub mod entity; +pub mod identity; +pub mod label; +pub mod operation; + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum Status { + Open, + Closed, +} +impl From<&Value> for Status { + fn from(value: &Value) -> Self { + match value.as_u64().expect("This should be a integer") { + 1 => Self::Open, + 2 => Self::Closed, + other => unimplemented!("Invalid status string: '{other}'"), + } + } +} +impl Display for Status { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Status::Open => f.write_str("Open"), + Status::Closed => f.write_str("Closed"), + } + } +} + +#[derive(Debug)] +pub struct CollapsedIssue { + pub id: Id, + pub author: Author, + pub timestamp: TimeStamp, + pub title: MarkDown, + pub message: MarkDown, + pub comments: Vec<Comment>, + pub status: Status, + pub last_status_change: TimeStamp, + pub labels: Vec<Label>, +} +impl From<RawCollapsedIssue> for CollapsedIssue { + fn from(r: RawCollapsedIssue) -> Self { + macro_rules! get { + ($name:ident) => { + r.$name.expect(concat!( + "'", + stringify!($name), + "' is unset, when trying to collapes an issue! (This is likely a bug)" + )) + }; + } + + Self { + id: get! {id}, + author: get! {author}, + timestamp: get! {timestamp}, + title: get! {title}, + message: get! {message}, + comments: r.comments, + status: get! {status}, + last_status_change: get! {last_status_change}, + labels: r.labels, + } + } +} + +#[derive(Debug)] +pub struct Comment { + pub id: Id, + pub author: Author, + pub timestamp: TimeStamp, + pub message: MarkDown, +} + +#[derive(Debug, Default)] +pub struct RawCollapsedIssue { + pub id: Option<Id>, + pub author: Option<Author>, + pub timestamp: Option<TimeStamp>, + pub title: Option<MarkDown>, + pub message: Option<MarkDown>, + pub status: Option<Status>, + pub last_status_change: Option<TimeStamp>, + + // NOTE(@bpeetz): These values set here already, because an issue without these + // would be perfectly valid. <2024-12-26> + pub labels: Vec<Label>, + pub comments: Vec<Comment>, +} + +impl RawCollapsedIssue { + pub fn append_entity(&mut self, entity: Entity) { + for op in entity.operations { + match op { + Operation::AddComment { timestamp, message } => { + self.comments.push(Comment { + id: entity.id.clone(), + author: entity.author.clone(), + timestamp, + message, + }); + } + Operation::Create { + timestamp, + title, + message, + } => { + self.id = Some(entity.id.clone()); + self.author = Some(entity.author.clone()); + self.timestamp = Some(timestamp.clone()); + self.title = Some(title); + self.message = Some(message); + self.status = Some(Status::Open); // This is the default in git_bug + self.last_status_change = Some(timestamp); + } + Operation::EditComment { + timestamp, + target, + message, + } => { + let comments = &mut self.comments; + + let target_comment = comments + .iter_mut() + .find(|comment| comment.id == target) + .expect("The target must be a valid comment"); + + // TODO(@bpeetz): We should probably set a `edited = true` flag here. <2024-12-26> + // TODO(@bpeetz): Should we also change the author? <2024-12-26> + + target_comment.timestamp = timestamp; + target_comment.message = message; + } + Operation::LabelChange { + timestamp: _, + added, + removed, + } => { + let labels = self.labels.clone(); + + self.labels = labels + .into_iter() + .filter(|val| !removed.contains(val)) + .chain(added.into_iter()) + .collect(); + } + Operation::SetStatus { timestamp, status } => { + self.status = Some(status); + self.last_status_change = Some(timestamp); + } + Operation::SetTitle { + timestamp: _, + title, + was: _, + } => { + self.title = Some(title); + } + + Operation::NoOp {} => unimplemented!(), + Operation::SetMetadata {} => unimplemented!(), + } + } + } +} |