1#![allow(unused_variables)]
2
3use crate::{State, permissions::PermissionGroup};
4use indexmap::IndexMap;
5use serde::Serialize;
6use std::{ops::Deref, sync::Arc};
7use utoipa::ToSchema;
8use utoipa_axum::router::OpenApiRouter;
9
10pub mod background_tasks;
11pub mod commands;
12pub mod distr;
13pub mod email_templates;
14pub mod manager;
15pub mod settings;
16pub mod shutdown_handlers;
17
18pub struct ExtensionRouteBuilder {
19 state: State,
20 pub global: Option<Box<OpenApiRouter<State>>>,
21 pub api_admin: Option<Box<OpenApiRouter<State>>>,
22 pub api_auth: Option<Box<OpenApiRouter<State>>>,
23 pub api_client: Option<Box<OpenApiRouter<State>>>,
24 pub api_client_servers_server: Option<Box<OpenApiRouter<State>>>,
25 pub api_remote: Option<Box<OpenApiRouter<State>>>,
26 pub api_remote_servers_server: Option<Box<OpenApiRouter<State>>>,
27}
28
29impl ExtensionRouteBuilder {
30 pub fn new(state: State) -> Self {
31 Self {
32 state,
33 global: None,
34 api_admin: None,
35 api_auth: None,
36 api_client: None,
37 api_client_servers_server: None,
38 api_remote: None,
39 api_remote_servers_server: None,
40 }
41 }
42
43 pub fn add_global_router(
45 mut self,
46 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
47 ) -> Self {
48 self.global = Some(Box::new(router(self.global.map_or_else(
49 || OpenApiRouter::new().with_state(self.state.clone()),
50 |b| *b,
51 ))));
52
53 self
54 }
55
56 pub fn add_admin_api_router(
59 mut self,
60 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
61 ) -> Self {
62 self.api_admin = Some(Box::new(router(self.api_admin.map_or_else(
63 || OpenApiRouter::new().with_state(self.state.clone()),
64 |b| *b,
65 ))));
66
67 self
68 }
69
70 pub fn add_auth_api_router(
72 mut self,
73 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
74 ) -> Self {
75 self.api_auth = Some(Box::new(router(self.api_auth.map_or_else(
76 || OpenApiRouter::new().with_state(self.state.clone()),
77 |b| *b,
78 ))));
79
80 self
81 }
82
83 pub fn add_client_api_router(
86 mut self,
87 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
88 ) -> Self {
89 self.api_client = Some(Box::new(router(self.api_client.map_or_else(
90 || OpenApiRouter::new().with_state(self.state.clone()),
91 |b| *b,
92 ))));
93
94 self
95 }
96
97 pub fn add_client_server_api_router(
100 mut self,
101 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
102 ) -> Self {
103 self.api_client_servers_server = Some(Box::new(router(
104 self.api_client_servers_server.map_or_else(
105 || OpenApiRouter::new().with_state(self.state.clone()),
106 |b| *b,
107 ),
108 )));
109
110 self
111 }
112
113 pub fn add_remote_api_router(
116 mut self,
117 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
118 ) -> Self {
119 self.api_remote = Some(Box::new(router(self.api_remote.map_or_else(
120 || OpenApiRouter::new().with_state(self.state.clone()),
121 |b| *b,
122 ))));
123
124 self
125 }
126
127 pub fn add_remote_server_api_router(
130 mut self,
131 router: impl FnOnce(OpenApiRouter<State>) -> OpenApiRouter<State>,
132 ) -> Self {
133 self.api_remote_servers_server = Some(Box::new(router(
134 self.api_remote_servers_server.map_or_else(
135 || OpenApiRouter::new().with_state(self.state.clone()),
136 |b| *b,
137 ),
138 )));
139
140 self
141 }
142}
143
144type RawPermissionMap = IndexMap<&'static str, PermissionGroup>;
145pub struct ExtensionPermissionsBuilder {
146 pub user_permissions: RawPermissionMap,
147 pub admin_permissions: RawPermissionMap,
148 pub server_permissions: RawPermissionMap,
149}
150
151impl ExtensionPermissionsBuilder {
152 pub fn new(
153 user_permissions: RawPermissionMap,
154 admin_permissions: RawPermissionMap,
155 server_permissions: RawPermissionMap,
156 ) -> Self {
157 Self {
158 user_permissions,
159 admin_permissions,
160 server_permissions,
161 }
162 }
163
164 pub fn add_user_permission_group(
166 mut self,
167 group_name: &'static str,
168 group: PermissionGroup,
169 ) -> Self {
170 self.user_permissions.insert(group_name, group);
171
172 self
173 }
174
175 pub fn mutate_user_permission_group(
177 mut self,
178 group_name: &'static str,
179 mutation: impl FnOnce(&mut PermissionGroup),
180 ) -> Self {
181 if let Some(group) = self.user_permissions.get_mut(group_name) {
182 mutation(group);
183 }
184
185 self
186 }
187
188 pub fn add_admin_permission_group(
190 mut self,
191 group_name: &'static str,
192 group: PermissionGroup,
193 ) -> Self {
194 self.admin_permissions.insert(group_name, group);
195
196 self
197 }
198
199 pub fn mutate_admin_permission_group(
201 mut self,
202 group_name: &'static str,
203 mutation: impl FnOnce(&mut PermissionGroup),
204 ) -> Self {
205 if let Some(group) = self.admin_permissions.get_mut(group_name) {
206 mutation(group);
207 }
208
209 self
210 }
211
212 pub fn add_server_permission_group(
214 mut self,
215 group_name: &'static str,
216 group: PermissionGroup,
217 ) -> Self {
218 self.server_permissions.insert(group_name, group);
219
220 self
221 }
222
223 pub fn mutate_server_permission_group(
225 mut self,
226 group_name: &'static str,
227 mutation: impl FnOnce(&mut PermissionGroup),
228 ) -> Self {
229 if let Some(group) = self.server_permissions.get_mut(group_name) {
230 mutation(group);
231 }
232
233 self
234 }
235}
236
237pub struct ExtensionUpdateInfo {
238 pub version: semver::Version,
239 pub changes: Vec<compact_str::CompactString>,
240}
241
242pub type ExtensionCallValue = Box<dyn std::any::Any + Send + Sync>;
243
244#[async_trait::async_trait]
245pub trait Extension: Send + Sync {
246 async fn initialize(&mut self, state: State) {}
248
249 async fn initialize_cli(
251 &mut self,
252 env: Option<&Arc<crate::env::Env>>,
253 builder: commands::CliCommandGroupBuilder,
254 ) -> commands::CliCommandGroupBuilder {
255 builder
256 }
257
258 async fn initialize_router(
260 &mut self,
261 state: State,
262 builder: ExtensionRouteBuilder,
263 ) -> ExtensionRouteBuilder {
264 builder
265 }
266
267 async fn initialize_email_templates(
269 &mut self,
270 state: State,
271 builder: email_templates::ExtensionEmailTemplateBuilder,
272 ) -> email_templates::ExtensionEmailTemplateBuilder {
273 builder
274 }
275
276 async fn initialize_background_tasks(
278 &mut self,
279 state: State,
280 builder: background_tasks::BackgroundTaskBuilder,
281 ) -> background_tasks::BackgroundTaskBuilder {
282 builder
283 }
284
285 async fn initialize_shutdown_handlers(
287 &mut self,
288 state: State,
289 builder: shutdown_handlers::ShutdownHandlerBuilder,
290 ) -> shutdown_handlers::ShutdownHandlerBuilder {
291 builder
292 }
293
294 async fn initialize_permissions(
296 &mut self,
297 state: State,
298 builder: ExtensionPermissionsBuilder,
299 ) -> ExtensionPermissionsBuilder {
300 builder
301 }
302
303 async fn settings_deserializer(&self, state: State) -> settings::ExtensionSettingsDeserializer {
307 Arc::new(settings::EmptySettings)
308 }
309
310 async fn check_for_updates(
314 &self,
315 state: State,
316 current_version: &semver::Version,
317 ) -> Result<Option<ExtensionUpdateInfo>, anyhow::Error> {
318 Ok(None)
319 }
320
321 async fn process_call(
326 &self,
327 name: &str,
328 args: &[ExtensionCallValue],
329 ) -> Option<ExtensionCallValue> {
330 None
331 }
332
333 async fn process_call_owned(
340 &self,
341 name: &str,
342 args: Vec<ExtensionCallValue>,
343 ) -> Option<ExtensionCallValue> {
344 self.process_call(name, &args).await
345 }
346}
347
348#[derive(ToSchema, Serialize, Clone)]
349pub struct ConstructedExtension {
350 pub metadata_toml: distr::MetadataToml,
351 pub package_name: &'static str,
352 pub description: &'static str,
353 pub authors: &'static [&'static str],
354 #[schema(value_type = String)]
355 pub version: semver::Version,
356
357 #[serde(skip)]
358 #[schema(ignore)]
359 pub extension: Arc<dyn Extension>,
360}
361
362impl Deref for ConstructedExtension {
363 type Target = Arc<dyn Extension>;
364
365 fn deref(&self) -> &Self::Target {
366 &self.extension
367 }
368}
369
370#[derive(ToSchema, Serialize, Clone)]
371pub struct PendingExtension {
372 pub metadata_toml: distr::MetadataToml,
373 pub package_name: compact_str::CompactString,
374 pub description: compact_str::CompactString,
375 pub authors: Vec<compact_str::CompactString>,
376 #[schema(value_type = String)]
377 pub version: semver::Version,
378}