Skip to main content

vissue_core/
views.rs

1//! Typed issue views shared by JSON output and later control clients.
2
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5use std::path::PathBuf;
6
7use crate::model::IssueHeading;
8
9/// One parsed heading plus the `issues.org` it came from.
10#[derive(Debug, Clone)]
11pub struct IssueRec {
12    /// Project directory name the heading lives under.
13    pub project: String,
14    /// Parsed heading, including body and logbook.
15    pub heading: IssueHeading,
16    /// Absolute path of the project's `issues.org`.
17    pub path: PathBuf,
18    /// File-level tags and `#+TAGS:` groups from the preamble.
19    pub tag_settings: crate::org::TagSettings,
20}
21
22/// Filters for [`crate::catalog::CatalogService::issues_rows`].
23#[derive(Debug, Clone, Default, PartialEq, Eq)]
24pub struct ListQuery {
25    /// Restrict to this project name (case-insensitive).
26    pub project: Option<String>,
27    /// Restrict to this TODO keyword.
28    pub state: Option<String>,
29    /// Keep only TODO or STARTED issues with no open blocker.
30    pub ready: bool,
31    /// Case-insensitive substring over id, title, tags, and properties.
32    pub query: Option<String>,
33    /// Cap the result after sorting.
34    pub limit: Option<usize>,
35    /// Drop this many leading rows after sorting.
36    pub offset: Option<usize>,
37}
38
39/// One list/ready row: the fields a board or JSON client paints.
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
41pub struct IssueRow {
42    /// Issue id, `<project>-<suffix>`.
43    pub id: String,
44    /// TODO keyword on the heading.
45    pub state: String,
46    /// Priority cookie as a one-character string.
47    pub priority: String,
48    /// Heading title, without tags.
49    pub title: String,
50    /// Project the heading lives in.
51    pub project: String,
52    /// Ids listed in `:BLOCKED_BY:`.
53    pub blocked_by: Vec<String>,
54    /// Identity holding the issue, when claimed.
55    pub claimed_by: Option<String>,
56    /// Org timestamp of the claim.
57    pub claimed_at: Option<String>,
58    /// `:PARENT:` id, when set.
59    #[serde(default)]
60    pub parent: Option<String>,
61}
62
63/// One issue as a detail card: properties, tags, file range, body, and logbook.
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
65pub struct IssueDetail {
66    /// Issue id, `<project>-<suffix>`.
67    pub id: String,
68    /// Project the heading lives in.
69    pub project: String,
70    /// Heading title, without tags.
71    pub title: String,
72    /// TODO keyword on the heading.
73    pub state: String,
74    /// Priority cookie as a one-character string.
75    pub priority: String,
76    /// Property drawer, including planning keys held in the map.
77    pub properties: BTreeMap<String, String>,
78    /// Tags written on the heading itself.
79    pub org_tags: Vec<String>,
80    /// Combined heading tags and `:VISSUE_TAGS:`.
81    pub tags: Vec<String>,
82    /// Ids listed in `:BLOCKED_BY:`.
83    pub blocked_by: Vec<String>,
84    /// `:PARENT:` id, when set.
85    pub parent: Option<String>,
86    /// Identity holding the issue, when claimed.
87    pub claimed_by: Option<String>,
88    /// Org timestamp of the claim.
89    pub claimed_at: Option<String>,
90    /// `path:line_start-line_end` of the heading in its `issues.org`.
91    pub file: String,
92    /// 1-based first line of the heading in the file.
93    pub line_start: usize,
94    /// 1-based last line of the heading in the file.
95    pub line_end: usize,
96    /// Prose under the heading, without the property drawer or logbook.
97    ///
98    /// Carried here so a caller that fetched the detail has what the issue
99    /// asks for, rather than a file path and a line range to go read.
100    #[serde(default)]
101    pub body: String,
102    /// Logbook lines on the heading, newest first.
103    #[serde(default)]
104    pub logbook: Vec<LogbookLine>,
105}
106
107/// One logbook line on a detail card: note, state flip, or raw CLOCK.
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
109pub struct LogbookLine {
110    /// Inactive org timestamp on the line, or empty for a raw CLOCK row.
111    #[serde(default)]
112    pub timestamp: String,
113    /// Previous TODO keyword on a state flip.
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub from_state: Option<String>,
116    /// New TODO keyword on a state flip.
117    #[serde(default, skip_serializing_if = "Option::is_none")]
118    pub to_state: Option<String>,
119    /// Folded note text, when the line is a note rather than a state flip.
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub note: Option<String>,
122    /// Opaque drawer line preserved verbatim (a `CLOCK:` entry, say).
123    #[serde(default, skip_serializing_if = "Option::is_none")]
124    pub raw: Option<String>,
125}
126
127/// One live claim: who holds the issue and for how long.
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
129pub struct ClaimRow {
130    /// Issue id.
131    pub id: String,
132    /// Project the heading lives in.
133    pub project: String,
134    /// TODO keyword on the heading.
135    pub state: String,
136    /// Priority cookie as a one-character string.
137    pub priority: String,
138    /// Identity holding the issue.
139    pub holder: Option<String>,
140    /// Org timestamp of the claim.
141    pub claimed_at: Option<String>,
142    /// Whole days since the claim; `-1` when the stamp does not parse.
143    pub age_days: i64,
144    /// Heading title.
145    pub title: String,
146}
147
148/// A capped, secret-screened slice of a heading's on-disk range.
149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
150pub struct Excerpt {
151    /// Issue id.
152    pub id: String,
153    /// Path of the `issues.org` the heading lives in.
154    pub file: String,
155    /// 1-based first line of the heading.
156    pub line_start: usize,
157    /// 1-based last line of the heading.
158    pub line_end: usize,
159    /// Excerpt text, or a suppression notice when credential-shaped.
160    pub text: String,
161    /// Whether `text` is a suppression notice rather than the heading.
162    pub suppressed: bool,
163}
164
165/// One search match: the heading plus a short snippet of the hit.
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
167pub struct SearchHit {
168    /// Issue id.
169    pub id: String,
170    /// Project the heading lives in.
171    pub project: String,
172    /// TODO keyword on the heading.
173    pub state: String,
174    /// Priority cookie as a one-character string.
175    pub priority: String,
176    /// Heading title.
177    pub title: String,
178    /// First matching line, capped.
179    pub snippet: String,
180}
181
182/// One dated row: a deadline or scheduled date on an open issue.
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
184pub struct AgendaRow {
185    /// Calendar date as `YYYY-MM-DD`.
186    pub date: String,
187    /// `deadline` or `scheduled`.
188    pub kind: String,
189    /// Days past the date; `0` when it is today or still upcoming.
190    pub overdue_days: i64,
191    /// Issue id.
192    pub id: String,
193    /// Project the heading lives in.
194    pub project: String,
195    /// TODO keyword on the heading.
196    pub state: String,
197    /// Priority cookie as a one-character string.
198    pub priority: String,
199    /// Heading title.
200    pub title: String,
201}
202
203/// A parent/child subtree node, with the issue's own blockers.
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
205pub struct TreeNode {
206    /// Issue id.
207    pub id: String,
208    /// TODO keyword on the heading.
209    pub state: String,
210    /// Heading title.
211    pub title: String,
212    /// Direct children by `:PARENT:`.
213    pub children: Vec<TreeNode>,
214    /// Ids listed in `:BLOCKED_BY:`.
215    pub blocked_by: Vec<String>,
216}
217
218/// One ranked related-issue hit, with the evidence that produced the score.
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
220pub struct RelatedHit {
221    /// Issue id.
222    pub id: String,
223    /// Project the heading lives in.
224    pub project: String,
225    /// TODO keyword on the heading.
226    pub state: String,
227    /// Heading title.
228    pub title: String,
229    /// Combined evidence score; higher is a closer match.
230    pub score: f64,
231    /// Named reasons (`blocked_by`, `term:foo`, `org_distance:1`, ...).
232    pub evidence: Vec<String>,
233}
234
235/// One related heading from a walk: children, ancestors, impact, or backlinks.
236#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
237pub struct WalkHit {
238    /// Issue id.
239    pub id: String,
240    /// Project the heading lives in.
241    pub project: String,
242    /// TODO keyword on the heading.
243    pub state: String,
244    /// Heading title.
245    pub title: String,
246    /// How this heading relates to the walk root (`child`, `ancestor`, ...).
247    pub relation: String,
248}