1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#![cfg_attr(coverage_nightly, coverage(off))]
//! GitHub Issues Integration Service
//!
//! This module provides comprehensive GitHub Issues API integration with support for:
//! - Issue creation, reading, updating, and listing
//! - Authentication via GitHub tokens and OAuth
//! - Rate limiting and error recovery
//! - PDMT-style issue template generation
//! - Quality-proxy integration for automated refactoring workflows
//!
//! # Features
//!
//! - **Full GitHub API Support**: REST API v3 with GraphQL v4 capabilities
//! - **Authentication**: Token-based, OAuth, and GitHub App authentication
//! - **Rate Limiting**: Automatic retry with exponential backoff
//! - **Error Recovery**: Comprehensive error handling with user-friendly messages
//! - **PDMT Integration**: Deterministic issue generation using seed 42
//! - **Quality Enforcement**: Integration with quality-proxy for code generation
//!
//! See [`GitHubIssuesService`] for usage examples.
use reqwest::{Client, Error as ReqwestError};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error;
use tokio::time::sleep;
/// Errors that can occur when working with GitHub Issues
#[derive(Error, Debug)]
pub enum GitHubError {
#[error("HTTP request failed: {0}")]
Request(#[from] ReqwestError),
#[error("Authentication failed: {token_type}")]
Authentication { token_type: String },
#[error("Rate limit exceeded, retry after {retry_after} seconds")]
RateLimit { retry_after: u64 },
#[error("GitHub API error: {status} - {message}")]
Api { status: u16, message: String },
#[error("Invalid repository format: {repo}")]
InvalidRepo { repo: String },
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
}
/// GitHub Issue representation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GitHubIssue {
pub id: u64,
pub number: u32,
pub title: String,
pub body: Option<String>,
pub state: IssueState,
pub labels: Vec<Label>,
pub assignees: Vec<User>,
pub created_at: String,
pub updated_at: String,
pub html_url: String,
pub user: User,
}
/// Issue state enumeration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum IssueState {
Open,
Closed,
}
/// GitHub label representation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Label {
pub id: u64,
pub name: String,
pub color: String,
pub description: Option<String>,
}
/// GitHub user representation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: u64,
pub login: String,
pub avatar_url: String,
pub html_url: String,
}
/// Request payload for creating new issues
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssueRequest {
pub title: String,
pub body: String,
pub labels: Vec<String>,
pub assignees: Vec<String>,
}
/// Request payload for updating existing issues
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssueUpdateRequest {
pub title: Option<String>,
pub body: Option<String>,
pub state: Option<IssueState>,
pub labels: Option<Vec<String>>,
pub assignees: Option<Vec<String>>,
}
impl Default for IssueUpdateRequest {
fn default() -> Self {
Self {
title: None,
body: None,
state: None,
labels: None,
assignees: None,
}
}
}
/// GitHub API pagination information
#[derive(Debug, Clone)]
pub struct Pagination {
pub page: u32,
pub per_page: u32,
}
impl Default for Pagination {
fn default() -> Self {
Self {
page: 1,
per_page: 30,
}
}
}
/// Configuration for GitHub Issues service
#[derive(Debug, Clone)]
pub struct GitHubConfig {
pub token: String,
pub base_url: String,
pub timeout: Duration,
pub max_retries: u32,
pub retry_delay: Duration,
}
impl Default for GitHubConfig {
fn default() -> Self {
Self {
token: String::new(),
base_url: "https://api.github.com".to_string(),
timeout: Duration::from_secs(30),
max_retries: 3,
retry_delay: Duration::from_secs(1),
}
}
}
/// Main service for GitHub Issues integration
///
/// Provides comprehensive GitHub Issues API integration with authentication,
/// rate limiting, error recovery, and PDMT-style issue generation.
///
/// # Examples
///
/// ```rust,no_run
/// use pmat::services::github_issues::GitHubIssuesService;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let service = GitHubIssuesService::new("github_token_here")?;
/// let issues = service.list_issues("owner", "repo", None).await?;
/// println!("Found {} issues", issues.len());
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone)]
pub struct GitHubIssuesService {
client: Client,
config: GitHubConfig,
}
// Client implementation (constructors, CRUD, retry logic, response handling)
include!("github_issues_client.rs");
// Unit tests and property tests
include!("github_issues_tests.rs");