use std::collections::BTreeMap;
use serde::Serialize;
use crate::domain::model::issue::Issue;
use crate::domain::model::status::RollupHistogram;
use crate::domain::usecases::issue::TagRollupValue;
#[derive(Serialize)]
pub struct IssueView<'a> {
pub id: String,
pub title: &'a str,
pub status: &'a str,
pub date: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub children: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rollup: Option<RollupView>,
pub content: &'a str,
pub events: EventLogView<'a>,
}
#[derive(Serialize, Default)]
pub struct RollupView {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<StatusRollupView>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub tags: BTreeMap<String, TagRollupValue>,
}
#[derive(Serialize)]
pub struct StatusRollupView {
pub category: String,
pub histogram: RollupHistogram,
}
impl RollupView {
pub fn from_status(h: RollupHistogram) -> Self {
RollupView {
status: Some(StatusRollupView {
category: h.category().as_str().to_string(),
histogram: h,
}),
tags: BTreeMap::new(),
}
}
pub fn with_tag_rollups(mut self, tags: BTreeMap<String, TagRollupValue>) -> Self {
self.tags = tags;
self
}
}
#[derive(Serialize)]
pub struct EventLogView<'a>(Vec<EventView<'a>>);
#[derive(Serialize)]
pub struct EventView<'a> {
pub timestamp: &'a str,
pub action: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
}
impl<'a> IssueView<'a> {
pub fn from_issue(issue: &'a Issue) -> Self {
use crate::domain::model::event::EventAction;
let events = EventLogView(
issue
.events
.iter()
.map(|e| {
let (from, to) = match &e.action {
EventAction::Created { state } => (None, Some(state.as_str().to_string())),
EventAction::StatusChanged { from, to } => (
Some(from.as_str().to_string()),
Some(to.as_str().to_string()),
),
};
EventView {
timestamp: e.timestamp.as_str(),
action: e.action.as_str(),
from,
to,
}
})
.collect(),
);
IssueView {
id: issue.id.to_string(),
title: issue.title.as_str(),
status: issue.status.as_str(),
date: issue.date.to_string(),
tags: issue.tags.iter().map(|t| t.as_str()).collect(),
parent: None,
children: Vec::new(),
rollup: None,
content: issue.content.as_str(),
events,
}
}
pub fn with_family(mut self, family: &crate::domain::usecases::issue::IssueFamily) -> Self {
self.parent = family.parent.as_ref().map(|r| r.to_string());
self.children = family.children.iter().map(|r| r.to_string()).collect();
let mut view = match family.rollup {
Some(h) => RollupView::from_status(h),
None if family.tag_rollups.is_empty() => return self,
None => RollupView::default(),
};
view = view.with_tag_rollups(family.tag_rollups.clone());
self.rollup = Some(view);
self
}
pub fn with_rollups(
mut self,
status: Option<RollupHistogram>,
tags: BTreeMap<String, TagRollupValue>,
) -> Self {
if status.is_none() && tags.is_empty() {
return self;
}
let mut view = match status {
Some(h) => RollupView::from_status(h),
None => RollupView::default(),
};
view = view.with_tag_rollups(tags);
self.rollup = Some(view);
self
}
}