mockforge_intelligence/handlers/
pr_generation.rs1use axum::extract::State;
6use axum::http::StatusCode;
7use axum::response::Json;
8use serde::{Deserialize, Serialize};
9use std::sync::Arc;
10
11use crate::pr_generation::{PRFileChange, PRFileChangeType, PRGenerator, PRTemplateContext};
12
13#[derive(Clone)]
15pub struct PRGenerationState {
16 pub generator: Option<Arc<PRGenerator>>,
18}
19
20#[derive(Debug, Deserialize, Serialize)]
22pub struct GeneratePRRequest {
23 pub endpoint: String,
25 pub method: String,
27 pub breaking_changes: u32,
29 pub non_breaking_changes: u32,
31 pub change_summary: String,
33 pub affected_files: Vec<String>,
35 pub file_changes: Vec<PRFileChangeRequest>,
37 pub labels: Option<Vec<String>>,
39 pub reviewers: Option<Vec<String>>,
41}
42
43#[derive(Debug, Deserialize, Serialize)]
45pub struct PRFileChangeRequest {
46 pub path: String,
48 pub content: String,
50 pub change_type: String,
52}
53
54#[derive(Debug, Serialize)]
56pub struct GeneratePRResponse {
57 pub pr_number: Option<u64>,
59 pub pr_url: Option<String>,
61 pub branch: Option<String>,
63 pub success: bool,
65 pub error: Option<String>,
67}
68
69pub async fn generate_pr(
73 State(state): State<PRGenerationState>,
74 Json(request): Json<GeneratePRRequest>,
75) -> Result<Json<GeneratePRResponse>, StatusCode> {
76 let generator = state.generator.as_ref().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
77
78 let file_changes: Vec<PRFileChange> = request
80 .file_changes
81 .into_iter()
82 .map(|fc| {
83 let change_type = match fc.change_type.as_str() {
84 "create" => PRFileChangeType::Create,
85 "update" => PRFileChangeType::Update,
86 "delete" => PRFileChangeType::Delete,
87 _ => PRFileChangeType::Update,
88 };
89
90 PRFileChange {
91 path: fc.path,
92 content: fc.content,
93 change_type,
94 }
95 })
96 .collect();
97
98 let context = PRTemplateContext {
100 endpoint: request.endpoint,
101 method: request.method,
102 breaking_changes: request.breaking_changes,
103 non_breaking_changes: request.non_breaking_changes,
104 affected_files: request.affected_files,
105 change_summary: request.change_summary,
106 is_breaking: request.breaking_changes > 0,
107 metadata: std::collections::HashMap::new(),
108 };
109
110 match generator
112 .create_pr_from_context(
113 context,
114 file_changes,
115 request.labels.unwrap_or_default(),
116 request.reviewers.unwrap_or_default(),
117 )
118 .await
119 {
120 Ok(pr_result) => Ok(Json(GeneratePRResponse {
121 pr_number: Some(pr_result.number),
122 pr_url: Some(pr_result.url),
123 branch: Some(pr_result.branch),
124 success: true,
125 error: None,
126 })),
127 Err(e) => Ok(Json(GeneratePRResponse {
128 pr_number: None,
129 pr_url: None,
130 branch: None,
131 success: false,
132 error: Some(e.to_string()),
133 })),
134 }
135}
136
137pub fn pr_generation_router(state: PRGenerationState) -> axum::Router {
139 use axum::routing::post;
140
141 axum::Router::new()
142 .route("/api/v1/pr/generate", post(generate_pr))
143 .with_state(state)
144}