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