Skip to main content

aft/github_read/
model.rs

1use serde::{Deserialize, Serialize};
2
3use super::resource::GithubResourceKind;
4
5/// Structured GitHub data consumed by the canonical Rust renderer.
6///
7/// Fetchers normalize their provider-specific JSON into this model. Keeping the
8/// renderer independent of CLI field spelling prevents accidental parsing of
9/// human-oriented `gh` output and gives fixture tests a stable seam.
10#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
11#[serde(default, rename_all = "camelCase")]
12pub struct GithubDocument {
13    pub repository: String,
14    pub kind: GithubDocumentKind,
15    pub number: u64,
16    pub title: String,
17    pub state: String,
18    pub author: Option<String>,
19    pub created_at: Option<String>,
20    pub updated_at: Option<String>,
21    pub labels: Vec<String>,
22    pub assignees: Vec<String>,
23    pub milestone: Option<String>,
24    pub body: String,
25    pub reactions: Vec<GithubReaction>,
26    pub comments: Vec<GithubComment>,
27    /// Total issue-discussion comments before the renderer applies its cap.
28    pub comments_total_count: Option<usize>,
29    /// Server-supplied count when it includes minimized comments outside the
30    /// local page. The renderer also counts minimized comments it received.
31    pub minimized_comments_count: Option<usize>,
32    pub files: Vec<GithubPullRequestFile>,
33    pub reviews: Vec<GithubReview>,
34    pub review_comment_sections: Vec<GithubReviewCommentSection>,
35}
36
37impl GithubDocument {
38    pub fn resource_kind(&self) -> GithubResourceKind {
39        match self.kind {
40            GithubDocumentKind::Issue => GithubResourceKind::Issue,
41            GithubDocumentKind::PullRequest => GithubResourceKind::PullRequest,
42        }
43    }
44}
45
46/// Resource-kind mirror included in structured fixtures and normalized data.
47#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
48#[serde(rename_all = "snake_case")]
49pub enum GithubDocumentKind {
50    #[default]
51    Issue,
52    PullRequest,
53}
54
55#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
56#[serde(default, rename_all = "camelCase")]
57pub struct GithubReaction {
58    pub content: String,
59    pub count: u64,
60}
61
62#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
63#[serde(default, rename_all = "camelCase")]
64pub struct GithubComment {
65    pub author: Option<String>,
66    pub body: String,
67    pub created_at: Option<String>,
68    pub updated_at: Option<String>,
69    pub minimized: bool,
70}
71
72#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
73#[serde(default, rename_all = "camelCase")]
74pub struct GithubPullRequestFile {
75    pub path: String,
76    pub additions: Option<u64>,
77    pub deletions: Option<u64>,
78    pub status: Option<String>,
79}
80
81#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
82#[serde(default, rename_all = "camelCase")]
83pub struct GithubReview {
84    pub author: Option<String>,
85    pub body: String,
86    pub state: Option<String>,
87    pub submitted_at: Option<String>,
88    pub comments: Vec<GithubComment>,
89    pub comments_total_count: Option<usize>,
90}
91
92/// One review's inline-comment section. Each section is capped independently,
93/// so an active review cannot crowd comments out of a different review.
94#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
95#[serde(default, rename_all = "camelCase")]
96pub struct GithubReviewCommentSection {
97    pub author: Option<String>,
98    pub submitted_at: Option<String>,
99    pub comments: Vec<GithubComment>,
100    pub comments_total_count: Option<usize>,
101    pub minimized_comments_count: Option<usize>,
102}