Skip to main content

progit_plugin_sdk/
event.rs

1// SPDX-License-Identifier: LSL-1.0
2// Copyright (c) 2025 Markus Maiwald
3
4//! Canonical plugin event model.
5//!
6//! [ARCH] One event enum, one source of truth. Both the host and plugins
7//! consume `PluginEvent` from this crate. Earlier the host re-defined a
8//! parallel enum — that drift is gone as of v0.2.
9//!
10//! Two parallel surfaces coexist on purpose:
11//!
12//! - [`crate::traits::PluginHook`] is the *closed* hook enum used by the
13//!   convenience traits (`IssuePlugin`, `SyncPlugin`). Maps 1:1 to a Lua
14//!   function name.
15//! - [`PluginEvent`] is the *open* structured event used for richer
16//!   payloads and query-style interactions (e.g. `PipelineStatusQuery`).
17//!   Plugins handle it via `plugin.on_event(event)`.
18
19use serde::{Deserialize, Serialize};
20
21/// Plugin lifecycle and operational events.
22///
23/// Serialised as `{"type": "...", "data": {...}}` so Lua tables stay
24/// ergonomic (`event.type == "IssueCreated"`) and Rust code stays exhaustive.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26#[serde(tag = "type", content = "data")]
27pub enum PluginEvent {
28    /// ProGit just started.
29    Startup,
30    /// An issue was created.
31    IssueCreated { issue_id: String },
32    /// An issue was updated.
33    IssueUpdated { issue_id: String },
34    /// An issue's status changed.
35    IssueStatusChanged {
36        issue_id: String,
37        old_status: String,
38        new_status: String,
39    },
40    /// A commit was created.
41    CommitCreated { commit_hash: String },
42    /// A virtual branch was created.
43    BranchCreated { branch_id: String },
44    /// A virtual branch was updated.
45    BranchUpdated { branch_id: String },
46    /// An agent action ran on a branch.
47    AgentAction { action: String, branch_id: String },
48    /// CI pipeline status query for a merge request.
49    PipelineStatusQuery {
50        mr_id: String,
51        project_id: String,
52        source_branch: String,
53        target_branch: String,
54        /// `gitlab` | `github` | `forgejo`
55        forge_type: String,
56        api_url: String,
57    },
58    /// Plugin-to-plugin custom event.
59    Custom {
60        name: String,
61        payload: serde_json::Value,
62    },
63}
64
65/// CI/CD pipeline status — return type for `PipelineStatusQuery` events.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct PipelineStatus {
68    pub status: PipelineState,
69    pub pipeline_id: Option<String>,
70    pub jobs: Vec<PipelineJob>,
71    pub updated_at: Option<String>,
72    pub web_url: Option<String>,
73}
74
75/// Pipeline lifecycle state.
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(rename_all = "lowercase")]
78pub enum PipelineState {
79    Passed,
80    Failed,
81    Running,
82    Pending,
83    Canceled,
84    Skipped,
85    Unknown,
86}
87
88/// One job in a pipeline.
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct PipelineJob {
91    pub name: String,
92    pub status: PipelineState,
93    /// Free-form duration string from the forge (e.g. `"2m 14s"`).
94    pub duration: Option<String>,
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn event_round_trips_through_json() {
103        let evt = PluginEvent::IssueStatusChanged {
104            issue_id: "abc".into(),
105            old_status: "todo".into(),
106            new_status: "done".into(),
107        };
108        let s = serde_json::to_string(&evt).unwrap();
109        assert!(s.contains(r#""type":"IssueStatusChanged""#));
110        let back: PluginEvent = serde_json::from_str(&s).unwrap();
111        match back {
112            PluginEvent::IssueStatusChanged { new_status, .. } => assert_eq!(new_status, "done"),
113            _ => panic!("variant lost in round-trip"),
114        }
115    }
116
117    #[test]
118    fn pipeline_state_lowercases() {
119        let s = serde_json::to_string(&PipelineState::Passed).unwrap();
120        assert_eq!(s, "\"passed\"");
121    }
122}