progit_plugin_sdk/traits/
core.rs1use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct PluginMetadata {
14 pub name: String,
15 pub version: String,
16 pub author: String,
17 pub description: String,
18 pub hooks: Vec<PluginHook>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, Eq)]
23pub enum PluginHook {
24 OnIssueCreated,
27 OnIssueUpdated,
29 OnIssueDeleted,
31 OnStatusChanged,
33
34 OnSyncPush,
37 OnSyncPull,
39
40 OnMergeRequestCreated,
43
44 OnCommand(String),
47
48 OnSchedule(String),
51
52 OnBulkOperation(BulkOp),
55
56 OnExternalSync,
59 OnWebhookReceived,
61
62 OnSprintStart(u32),
65 OnSprintEnd(u32),
67 OnDueDateApproaching,
69 OnDueDatePassed,
71
72 OnReportRequested,
75 OnMetricQuery,
77}
78
79impl PartialEq for PluginHook {
82 fn eq(&self, other: &Self) -> bool {
83 match (self, other) {
84 (PluginHook::OnCommand(_), PluginHook::OnCommand(_)) => true,
85 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
86 }
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
92pub enum BulkOp {
93 Import,
94 Export,
95 Archive,
96 Delete,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct Issue {
102 pub id: String,
103 pub title: String,
104 pub description: String,
105 pub status: String,
106 pub tags: Vec<String>,
107 pub assignee: Option<String>,
108 pub effort: Option<u8>,
109 pub blocked: bool,
110 pub created: String,
111 pub updated: String,
112 pub due: Option<String>,
113 pub metadata: HashMap<String, serde_json::Value>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct PluginContext {
119 pub repo_path: String,
121 pub user: Option<String>,
123 pub env: HashMap<String, String>,
125 pub config: HashMap<String, serde_json::Value>,
127}
128
129pub type PluginResult<T> = Result<T, PluginError>;
131
132#[derive(Debug, thiserror::Error)]
134pub enum PluginError {
135 #[error("Plugin initialization failed: {0}")]
136 InitError(String),
137
138 #[error("Plugin execution failed: {0}")]
139 ExecutionError(String),
140
141 #[error("Invalid plugin configuration: {0}")]
142 ConfigError(String),
143
144 #[error("Hook not supported: {0:?}")]
145 UnsupportedHook(PluginHook),
146
147 #[error("Serialization error: {0}")]
148 SerializationError(#[from] serde_json::Error),
149
150 #[error("IO error: {0}")]
151 IoError(#[from] std::io::Error),
152
153 #[error("Storage error: {0}")]
154 StorageError(String),
155
156 #[error("Sync error: {0}")]
157 SyncError(String),
158
159 #[error("External API error: {0}")]
160 ExternalApiError(String),
161}
162
163pub trait Plugin {
168 fn metadata(&self) -> &PluginMetadata;
170
171 fn init(&mut self, context: &PluginContext) -> PluginResult<()>;
173
174 fn execute_hook(
176 &mut self,
177 hook: &PluginHook,
178 data: &serde_json::Value,
179 ) -> PluginResult<serde_json::Value>;
180
181 fn supports_hook(&self, hook: &PluginHook) -> bool {
183 self.metadata().hooks.contains(hook)
184 }
185
186 fn on_event(&mut self, event: &serde_json::Value) -> PluginResult<Option<serde_json::Value>> {
196 let _ = event; Ok(None)
199 }
200
201 fn highlight(
213 &mut self,
214 request: &crate::render::HighlightRequest,
215 ) -> PluginResult<Option<crate::render::HighlightResponse>> {
216 let _ = request;
217 Ok(None)
218 }
219}
220
221pub trait IssuePlugin: Plugin {
223 fn on_issue_created(&mut self, issue: &Issue) -> PluginResult<()> {
224 let data = serde_json::to_value(issue)?;
225 self.execute_hook(&PluginHook::OnIssueCreated, &data)?;
226 Ok(())
227 }
228
229 fn on_issue_updated(&mut self, issue: &Issue) -> PluginResult<()> {
230 let data = serde_json::to_value(issue)?;
231 self.execute_hook(&PluginHook::OnIssueUpdated, &data)?;
232 Ok(())
233 }
234
235 fn on_issue_deleted(&mut self, issue_id: &str) -> PluginResult<()> {
236 let data = serde_json::json!({ "id": issue_id });
237 self.execute_hook(&PluginHook::OnIssueDeleted, &data)?;
238 Ok(())
239 }
240}
241
242pub trait SyncPlugin: Plugin {
244 fn on_sync_push(&mut self, issues: &[Issue]) -> PluginResult<()> {
245 let data = serde_json::to_value(issues)?;
246 self.execute_hook(&PluginHook::OnSyncPush, &data)?;
247 Ok(())
248 }
249
250 fn on_sync_pull(&mut self, issues: &[Issue]) -> PluginResult<()> {
251 let data = serde_json::to_value(issues)?;
252 self.execute_hook(&PluginHook::OnSyncPull, &data)?;
253 Ok(())
254 }
255}