use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;
use crate::model::IssueHeading;
#[derive(Debug, Clone)]
pub struct IssueRec {
pub project: String,
pub heading: IssueHeading,
pub path: PathBuf,
pub tag_settings: crate::org::TagSettings,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ListQuery {
pub project: Option<String>,
pub state: Option<String>,
pub ready: bool,
pub query: Option<String>,
pub limit: Option<usize>,
pub offset: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct IssueRow {
pub id: String,
pub state: String,
pub priority: String,
pub title: String,
pub project: String,
pub blocked_by: Vec<String>,
pub claimed_by: Option<String>,
pub claimed_at: Option<String>,
#[serde(default)]
pub parent: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct IssueDetail {
pub id: String,
pub project: String,
pub title: String,
pub state: String,
pub priority: String,
pub properties: BTreeMap<String, String>,
pub org_tags: Vec<String>,
pub tags: Vec<String>,
pub blocked_by: Vec<String>,
#[serde(default)]
pub deeds: Vec<String>,
pub parent: Option<String>,
pub claimed_by: Option<String>,
pub claimed_at: Option<String>,
pub file: String,
pub line_start: usize,
pub line_end: usize,
#[serde(default)]
pub body: String,
#[serde(default)]
pub logbook: Vec<LogbookLine>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct LogbookLine {
#[serde(default)]
pub timestamp: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_state: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub to_state: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ClaimRow {
pub id: String,
pub project: String,
pub state: String,
pub priority: String,
pub holder: Option<String>,
pub claimed_at: Option<String>,
pub age_days: i64,
pub title: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Excerpt {
pub id: String,
pub file: String,
pub line_start: usize,
pub line_end: usize,
pub text: String,
pub suppressed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SearchHit {
pub id: String,
pub project: String,
pub state: String,
pub priority: String,
pub title: String,
pub snippet: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AgendaRow {
pub date: String,
pub kind: String,
pub overdue_days: i64,
pub id: String,
pub project: String,
pub state: String,
pub priority: String,
pub title: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct TreeNode {
pub id: String,
pub state: String,
pub title: String,
pub children: Vec<TreeNode>,
pub blocked_by: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RelatedHit {
pub id: String,
pub project: String,
pub state: String,
pub title: String,
pub score: f64,
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Recall {
pub id: String,
pub project: String,
pub state: String,
pub title: String,
pub plan: Vec<WalkHit>,
pub inputs: Vec<RecallInput>,
pub produced: Vec<String>,
pub body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PlanConsensus {
pub plan: String,
pub title: String,
pub children: Vec<ChildConsensus>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ChildConsensus {
pub id: String,
pub state: String,
pub title: String,
pub ballots: usize,
pub settling: Option<crate::consensus::Settling>,
pub holds: Option<(String, f64)>,
}
impl PlanConsensus {
#[must_use]
pub fn unvoted(&self) -> Vec<&ChildConsensus> {
self.children.iter().filter(|c| c.ballots == 0).collect()
}
#[must_use]
pub fn split(&self) -> Vec<&ChildConsensus> {
self.children
.iter()
.filter(|c| c.settling == Some(crate::consensus::Settling::Split))
.collect()
}
#[must_use]
pub fn settled(&self) -> bool {
self.split().is_empty() && self.unvoted().is_empty()
}
#[must_use]
pub fn positions(&self) -> Vec<&str> {
let mut seen: Vec<&str> = Vec::new();
for child in &self.children {
if let Some((choice, _)) = &child.holds
&& !seen.contains(&choice.as_str())
{
seen.push(choice.as_str());
}
}
seen
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RecallInput {
pub id: String,
pub project: String,
pub state: String,
pub title: String,
pub relation: String,
pub deeds: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub excerpt: Option<String>,
pub last_note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WalkHit {
pub id: String,
pub project: String,
pub state: String,
pub title: String,
pub relation: String,
}