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

ambee/giterated

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

Huge refactor to prep for moving the daemon over to the plugin architecture

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨5df753c

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