1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct GitHubIssue {
6 pub id: u64,
7 pub number: u64,
8 pub title: String,
9 pub body: Option<String>,
10 pub state: String,
11 pub labels: Vec<GitHubLabel>,
12 pub user: GitHubUser,
13 pub assignees: Vec<GitHubUser>,
14 pub created_at: DateTime<Utc>,
15 pub updated_at: DateTime<Utc>,
16 pub closed_at: Option<DateTime<Utc>>,
17 pub merged_at: Option<DateTime<Utc>>,
18 pub html_url: String,
19 pub is_pull_request: bool,
20 pub comments: u32,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct GitHubLabel {
25 pub id: u64,
26 pub name: String,
27 pub color: String,
28 pub description: Option<String>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct GitHubUser {
33 pub id: u64,
34 pub login: String,
35 pub avatar_url: String,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct GitHubComment {
40 pub id: u64,
41 pub user: GitHubUser,
42 pub body: String,
43 pub created_at: DateTime<Utc>,
44 pub updated_at: DateTime<Utc>,
45 pub html_url: String,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Repository {
50 pub owner: String,
51 pub name: String,
52 pub full_name: String,
53}
54
55impl Repository {
56 pub fn new(owner: impl Into<String>, name: impl Into<String>) -> Self {
57 let owner = owner.into();
58 let name = name.into();
59 let full_name = format!("{}/{}", owner, name);
60 Self {
61 owner,
62 name,
63 full_name,
64 }
65 }
66
67 pub fn from_url(url: &str) -> anyhow::Result<Self> {
68 let parts: Vec<&str> = url.trim_end_matches('/').split('/').collect();
69
70 if parts.len() < 2 {
71 return Err(anyhow::anyhow!("Invalid repository URL: {}", url));
72 }
73
74 let owner = parts[parts.len() - 2].to_string();
75 let name = parts[parts.len() - 1].to_string();
76
77 Ok(Self::new(owner, name))
78 }
79
80 pub fn from_full_name(full_name: &str) -> anyhow::Result<Self> {
81 let parts: Vec<&str> = full_name.split('/').collect();
82 if parts.len() != 2 {
83 return Err(anyhow::anyhow!(
84 "Invalid repository full name format. Expected 'owner/name', got: {}",
85 full_name
86 ));
87 }
88 Ok(Self::new(parts[0], parts[1]))
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct Discussion {
94 pub number: u64,
95 pub title: String,
96 pub body: String,
97 pub url: String,
98 pub author: GitHubUser,
99 pub created_at: DateTime<Utc>,
100 pub updated_at: DateTime<Utc>,
101 pub comments: Vec<DiscussionComment>,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct DiscussionComment {
106 pub id: String,
107 pub body: String,
108 pub author: GitHubUser,
109 pub created_at: DateTime<Utc>,
110 pub updated_at: DateTime<Utc>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct PrFile {
115 pub filename: String,
116 pub status: String,
117 pub additions: u32,
118 pub deletions: u32,
119 pub changes: u32,
120 pub patch: Option<String>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct CollectionResult {
125 pub repository: Repository,
126 pub issues: Vec<GitHubIssue>,
127 pub total_collected: usize,
128 pub collection_time: DateTime<Utc>,
129 pub filters_applied: Vec<String>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct PrReview {
135 pub id: u64,
136 pub user: GitHubUser,
137 pub body: Option<String>,
138 pub state: String,
140 pub submitted_at: Option<DateTime<Utc>>,
141 pub html_url: String,
142 pub commit_id: Option<String>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct PrReviewComment {
148 pub id: u64,
149 pub review_id: Option<u64>,
150 pub user: GitHubUser,
151 pub body: String,
152 pub path: String,
154 pub line: Option<u32>,
156 pub original_line: Option<u32>,
158 pub diff_hunk: String,
160 pub side: Option<String>,
162 pub commit_id: Option<String>,
163 pub created_at: DateTime<Utc>,
164 pub updated_at: DateTime<Utc>,
165 pub html_url: String,
166 pub position: Option<u32>,
168 pub in_reply_to_id: Option<u64>,
170}