JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

More progress :)

Amber - ⁨1⁩ year ago

parent: tbd commit: ⁨92c3f32

⁨plugins/giterated-issues/src/lib.rs⁩ - ⁨5509⁩ bytes
Raw
1 use std::{fmt::Display, str::FromStr, sync::OnceLock};
2
3 use anyhow::Error;
4 use giterated_models::{object::GiteratedObject, repository::Repository};
5 use giterated_plugin::{
6 handle::PluginInitializationState,
7 new_stack::{FFIPluginMeta, PluginState},
8 vtable::{HostVTable, InitializationVTable},
9 };
10 use giterated_plugin_sys::PluginStackBuilder;
11 use handlers::{
12 create_issue_request, edit_issue_request, issue_get_setting_contents, issue_get_setting_name,
13 issue_post_comment_request, issue_set_setting_contents, issue_set_setting_name,
14 issue_value_author, issue_value_comment_count, issue_value_creation_date, query_issues_request,
15 };
16 use serde::{Deserialize, Serialize};
17 use setting::NotificationsOverride;
18 use sqlx::{postgres::PgConnectOptions, PgPool};
19 use tokio::{fs::File, io::AsyncReadExt, runtime::Runtime};
20 use toml::Table;
21 use tracing::{debug, info};
22
23 pub mod db;
24 pub mod handlers;
25 pub mod operations;
26 pub mod setting;
27 pub mod value;
28
29 /// An issue, defined by the repository which owns it and its index.
30 ///
31 /// # Textual Format
32 /// An issue's textual format is defined as:
33 ///
34 /// `@{index: u32}:{repository: Repository}`
35 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
36 pub struct Issue {
37 pub repository: Repository,
38 pub id: u32,
39 }
40
41 impl Display for Issue {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.write_str(&format!("{}:{}", self.id, self.repository))
44 }
45 }
46
47 impl GiteratedObject for Issue {
48 fn object_name() -> &'static str {
49 "issue"
50 }
51
52 fn home_uri(&self) -> String {
53 self.repository.home_uri()
54 }
55
56 fn from_object_str(object_str: &str) -> Result<Self, Error> {
57 Ok(Issue::from_str(object_str)?)
58 }
59 }
60
61 impl FromStr for Issue {
62 type Err = IssueParseError;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 let (index, repository) = s.split_once(':').ok_or(IssueParseError)?;
66
67 let id: u32 = index.parse().map_err(|_| IssueParseError)?;
68 let repository = Repository::from_str(repository).map_err(|_| IssueParseError)?;
69
70 Ok(Self { repository, id })
71 }
72 }
73
74 #[derive(Debug, thiserror::Error)]
75 #[error("error parsing issue")]
76 pub struct IssueParseError;
77
78 static INIT_VTABLE: OnceLock<InitializationVTable> = OnceLock::new();
79 static ASYNC_RUNTIME: OnceLock<Runtime> = OnceLock::new();
80
81 #[no_mangle]
82 pub extern "C" fn plugin_meta() -> FFIPluginMeta {
83 const PLUGIN_NAME: &str = "Giterated [Issues]";
84 const PLUGIN_VERSION: &str = "0.1.0";
85
86 FFIPluginMeta {
87 name: PLUGIN_NAME.as_ptr(),
88 name_len: PLUGIN_NAME.len(),
89 version: PLUGIN_VERSION.as_ptr(),
90 version_len: PLUGIN_VERSION.len(),
91 }
92 }
93
94 #[no_mangle]
95 pub extern "C" fn load_host_vtable(_vtable: &HostVTable) {
96 println!("Loading vtable");
97 }
98
99 #[no_mangle]
100 pub extern "C" fn load_initialization_vtable(init_vtable: &InitializationVTable) {
101 INIT_VTABLE.set(*init_vtable).unwrap();
102 println!("Loaded initialization vtable");
103 }
104
105 #[no_mangle]
106 pub extern "C" fn initialize() -> PluginState {
107 // tracing_subscriber::fmt()
108 // .pretty()
109 // .with_thread_names(true)
110 // .with_max_level(Level::TRACE)
111 // .init();
112
113 PluginState {
114 inner: Box::into_raw(Box::new(())),
115 }
116 }
117
118 #[no_mangle]
119 pub extern "C" fn initialize_registration(
120 state: *mut PluginInitializationState,
121 ) -> *mut PluginInitializationState {
122 let runtime = Runtime::new().unwrap();
123
124 // let _guard: tracing::span::EnteredSpan = trace_span!("initialize_registration").entered();
125 let init_vtable = INIT_VTABLE.get().unwrap();
126
127 let db_pool = runtime.block_on(async {
128 let config: Table = {
129 let mut file = File::open("Giterated.toml").await.unwrap();
130 let mut text = String::new();
131 file.read_to_string(&mut text).await.unwrap();
132 text.parse().unwrap()
133 };
134 let db_conn_options = PgConnectOptions::new()
135 .host(config["postgres"]["host"].as_str().unwrap())
136 .port(config["postgres"]["port"].as_integer().unwrap() as u16)
137 .database(config["postgres"]["database"].as_str().unwrap())
138 .username(config["postgres"]["user"].as_str().unwrap())
139 .password(config["postgres"]["password"].as_str().unwrap());
140 let db_pool = PgPool::connect_with(db_conn_options).await.unwrap();
141
142 debug!("Running database migrations...");
143 // sqlx::migrate!().run(&db_pool).await.unwrap();
144 info!("Connected");
145
146 db_pool
147 });
148
149 ASYNC_RUNTIME.set(runtime).unwrap();
150
151 let plugin_state = IssuesPluginState { pool: db_pool };
152
153 let mut builder: PluginStackBuilder<'_, IssuesPluginState> =
154 PluginStackBuilder::new(plugin_state, state, init_vtable);
155
156 builder.object::<Issue>();
157
158 builder
159 .object_setting(issue_get_setting_name, issue_set_setting_name)
160 .object_setting(issue_get_setting_contents, issue_set_setting_contents);
161
162 builder.object_user_setting::<Issue, NotificationsOverride>();
163
164 builder
165 .value(issue_value_creation_date)
166 .value(issue_value_comment_count)
167 .value(issue_get_setting_name)
168 .value(issue_value_author);
169
170 builder
171 .operation(create_issue_request)
172 .operation(query_issues_request)
173 .operation(edit_issue_request)
174 .operation(issue_post_comment_request);
175
176 state
177 }
178
179 #[derive(Clone, Debug)]
180 pub struct IssuesPluginState {
181 pub pool: PgPool,
182 }
183