use crate::brain::Brain;
use crate::model::{Activity, Asked, Author, Changed, Comment, Kind, Task, fmt_ts, now};
use serde::Serialize;
#[derive(Debug, Serialize)]
#[non_exhaustive]
pub struct Entry<'a> {
pub id: u32,
pub kind: &'static str,
pub title: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decision: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub asked: Option<&'static str>,
pub tags: &'a [String],
#[serde(skip_serializing_if = "<[u32]>::is_empty")]
pub links: &'a [u32],
#[serde(skip_serializing_if = "Vec::is_empty")]
pub repo_links: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub linked_from: Vec<u32>,
pub created: String,
#[serde(skip_serializing_if = "str::is_empty")]
pub description: &'a str,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<EntryComment<'a>>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub activity: Vec<EntryChange<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tracked_minutes: Option<i64>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub running: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
}
#[derive(Debug, Serialize)]
#[non_exhaustive]
pub struct EntryComment<'a> {
pub created: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub edited: Option<String>,
pub body: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<&'static str>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub question: bool,
}
#[derive(Debug, Serialize)]
#[non_exhaustive]
pub struct EntryChange<'a> {
pub at: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<&'static str>,
pub changed: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<&'a str>,
#[serde(skip_serializing_if = "<[String]>::is_empty")]
pub added: &'a [String],
#[serde(skip_serializing_if = "<[String]>::is_empty")]
pub removed: &'a [String],
}
#[derive(Debug, Serialize)]
#[non_exhaustive]
pub struct Summary<'a> {
pub id: u32,
pub kind: &'static str,
pub title: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decision: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub asked: Option<&'static str>,
pub tags: &'a [String],
}
impl<'a> Entry<'a> {
pub fn new(brain: &Brain, task: &'a Task) -> Self {
let now = now();
Self {
id: task.id,
kind: task.kind.as_str(),
title: &task.title,
status: task.is_task().then(|| task.status.as_str()),
decision: decision(task),
asked: asked(task),
tags: &task.tags,
links: &task.links,
repo_links: task.repo_links.iter().map(ToString::to_string).collect(),
linked_from: brain.backlinks(task.id).iter().map(|t| t.id).collect(),
created: fmt_ts(task.created),
description: &task.description,
comments: task.comments.iter().map(EntryComment::new).collect(),
activity: task.activity.iter().map(EntryChange::new).collect(),
tracked_minutes: (!task.sessions.is_empty()).then(|| task.total(now).num_minutes()),
running: task.is_running(),
path: task.path.as_ref().map(|p| p.display().to_string()),
}
}
}
impl<'a> EntryComment<'a> {
fn new(comment: &'a Comment) -> Self {
Self {
created: fmt_ts(comment.created),
edited: comment.edited.map(fmt_ts),
body: &comment.body,
author: (comment.author != Author::Human).then(|| comment.author.as_str()),
question: comment.question,
}
}
}
impl<'a> EntryChange<'a> {
fn new(activity: &'a Activity) -> Self {
let none: &[String] = &[];
let (changed, from, to, added, removed) = match &activity.change {
Changed::Title { from, to } => ("title", Some(from.as_str()), Some(to.as_str()), none, none),
Changed::Tags { added, removed } => ("tags", None, None, added.as_slice(), removed.as_slice()),
Changed::Related { added, removed } => ("related", None, None, added.as_slice(), removed.as_slice()),
Changed::Description => ("description", None, None, none, none),
};
Self {
at: fmt_ts(activity.at),
author: (activity.by != Author::Human).then(|| activity.by.as_str()),
changed,
from,
to,
added,
removed,
}
}
}
impl<'a> Summary<'a> {
pub fn new(task: &'a Task) -> Self {
Self {
id: task.id,
kind: task.kind.as_str(),
title: &task.title,
status: task.is_task().then(|| task.status.as_str()),
decision: decision(task),
asked: asked(task),
tags: &task.tags,
}
}
}
fn decision(task: &Task) -> Option<&'static str> {
(task.kind == Kind::Decision).then(|| task.decision.as_str())
}
fn asked(task: &Task) -> Option<&'static str> {
task.asked().map(Asked::label)
}