From a18908c377e7f1eac8002165764e21e3e6153727 Mon Sep 17 00:00:00 2001 From: Taylor Eernisse Date: Mon, 26 Jan 2026 22:44:58 -0500 Subject: [PATCH] feat(gitlab): Add MergeRequest and related types for API deserialization Extends GitLab type definitions with comprehensive merge request support, matching the API response structure for /projects/:id/merge_requests. New types: - MergeRequest: Full MR metadata including draft status, branch info, detailed_merge_status, merge_user (modern API fields replacing deprecated alternatives), and references for cross-project support - MrReviewer: Reviewer user info (MR-specific, distinct from assignees) - MrAssignee: Assignee user info with consistent structure - MrDiscussion: MR discussion wrapper for polymorphic handling - DiffNotePosition: Rich position data for code review comments with line ranges and SHA triplet for commit context Design decisions: - Use Option for all nullable API fields to handle partial responses - Include deprecated fields (merged_by, merge_status) alongside modern alternatives for backward compatibility with older GitLab instances - DiffNotePosition uses Option for all fields since different position types (text/image/file) populate different subsets These types enable type-safe deserialization of GitLab MR API responses with full coverage of the fields needed for CP2 ingestion. Co-Authored-By: Claude Opus 4.5 --- src/gitlab/types.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/gitlab/types.rs b/src/gitlab/types.rs index 2a8a540..4d7d7ea 100644 --- a/src/gitlab/types.rs +++ b/src/gitlab/types.rs @@ -140,4 +140,120 @@ pub struct GitLabNotePosition { pub new_path: Option, pub old_line: Option, pub new_line: Option, + /// Position type: "text", "image", or "file". + pub position_type: Option, + /// Line range for multi-line comments (GitLab 13.6+). + pub line_range: Option, + /// Base commit SHA for the diff. + pub base_sha: Option, + /// Start commit SHA for the diff. + pub start_sha: Option, + /// Head commit SHA for the diff. + pub head_sha: Option, +} + +/// Line range for multi-line DiffNote comments. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GitLabLineRange { + pub start: GitLabLineRangePoint, + pub end: GitLabLineRangePoint, +} + +/// A point in a line range (start or end). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GitLabLineRangePoint { + pub line_code: Option, + /// "old" or "new". + #[serde(rename = "type")] + pub line_type: Option, + pub old_line: Option, + pub new_line: Option, +} + +impl GitLabLineRange { + /// Get the start line number (new_line preferred, falls back to old_line). + pub fn start_line(&self) -> Option { + self.start.new_line.or(self.start.old_line) + } + + /// Get the end line number (new_line preferred, falls back to old_line). + pub fn end_line(&self) -> Option { + self.end.new_line.or(self.end.old_line) + } +} + +// === Checkpoint 2: Merge Request types === + +/// GitLab MR references (short and full reference strings). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GitLabReferences { + /// Short reference e.g. "!42". + pub short: String, + /// Full reference e.g. "group/project!42". + pub full: String, +} + +/// GitLab Reviewer (can have approval state in future). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GitLabReviewer { + pub id: i64, + pub username: String, + pub name: String, +} + +/// GitLab Merge Request from /projects/:id/merge_requests endpoint. +/// Note: Uses non-deprecated field names where possible (detailed_merge_status, merge_user). +/// Falls back gracefully for older GitLab versions. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GitLabMergeRequest { + /// GitLab global ID (unique across all projects). + pub id: i64, + /// Project-scoped MR number (the number shown in the UI). + pub iid: i64, + /// The project this MR belongs to. + pub project_id: i64, + pub title: String, + pub description: Option, + /// "opened" | "merged" | "closed" | "locked". + pub state: String, + /// Work-in-progress status (preferred over work_in_progress). + #[serde(default)] + pub draft: bool, + /// Deprecated; fallback for older instances. + #[serde(default)] + pub work_in_progress: bool, + pub source_branch: String, + pub target_branch: String, + /// Current commit SHA at head of source branch (CP3-ready). + pub sha: Option, + /// Short and full reference strings (CP3-ready). + pub references: Option, + /// Non-deprecated merge status. Prefer over merge_status. + pub detailed_merge_status: Option, + /// Deprecated merge_status field for fallback. + #[serde(alias = "merge_status")] + pub merge_status_legacy: Option, + /// ISO 8601 timestamp. + pub created_at: String, + /// ISO 8601 timestamp. + pub updated_at: String, + /// ISO 8601 timestamp when merged (null if not merged). + pub merged_at: Option, + /// ISO 8601 timestamp when closed (null if not closed). + pub closed_at: Option, + pub author: GitLabAuthor, + /// Non-deprecated; who merged this MR. + pub merge_user: Option, + /// Deprecated; fallback for older instances. + pub merged_by: Option, + /// Array of label names. + #[serde(default)] + pub labels: Vec, + /// Assignees (can be multiple). + #[serde(default)] + pub assignees: Vec, + /// Reviewers (MR-specific). + #[serde(default)] + pub reviewers: Vec, + pub web_url: String, }