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}