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

ambee/giterated

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

So. Much. Work.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨b05f964

Showing ⁨⁨31⁩ changed files⁩ with ⁨⁨2747⁩ insertions⁩ and ⁨⁨8⁩ deletions⁩

Cargo.lock

View file
@@ -483,6 +483,29 @@ dependencies = [
483 483 ]
484 484
485 485 [[package]]
486 name = "dlopen2"
487 version = "0.6.1"
488 source = "registry+https://github.com/rust-lang/crates.io-index"
489 checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03"
490 dependencies = [
491 "dlopen2_derive",
492 "libc",
493 "once_cell",
494 "winapi",
495 ]
496
497 [[package]]
498 name = "dlopen2_derive"
499 version = "0.4.0"
500 source = "registry+https://github.com/rust-lang/crates.io-index"
501 checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
502 dependencies = [
503 "proc-macro2",
504 "quote",
505 "syn 2.0.38",
506 ]
507
508 [[package]]
486 509 name = "dotenvy"
487 510 version = "0.15.7"
488 511 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -549,6 +572,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
549 572 checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
550 573
551 574 [[package]]
575 name = "example-plugin"
576 version = "0.1.0"
577 dependencies = [
578 "anyhow",
579 "dlopen2",
580 "giterated-models",
581 "giterated-plugin",
582 "giterated-plugin-sys",
583 "serde_json",
584 "tracing",
585 "tracing-subscriber",
586 ]
587
588 [[package]]
552 589 name = "fastrand"
553 590 version = "2.0.1"
554 591 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -793,6 +830,20 @@ dependencies = [
793 830 ]
794 831
795 832 [[package]]
833 name = "giterated-issues"
834 version = "0.1.0"
835 dependencies = [
836 "anyhow",
837 "giterated-models",
838 "giterated-plugin",
839 "giterated-plugin-sys",
840 "serde",
841 "sqlx",
842 "thiserror",
843 "tokio",
844 ]
845
846 [[package]]
796 847 name = "giterated-models"
797 848 version = "0.1.0"
798 849 dependencies = [
@@ -817,6 +868,28 @@ dependencies = [
817 868 ]
818 869
819 870 [[package]]
871 name = "giterated-plugin"
872 version = "0.1.0"
873 dependencies = [
874 "anyhow",
875 "dlopen2",
876 "giterated-models",
877 "semver",
878 "serde_json",
879 "thiserror",
880 "tracing",
881 ]
882
883 [[package]]
884 name = "giterated-plugin-sys"
885 version = "0.1.0"
886 dependencies = [
887 "giterated-models",
888 "giterated-plugin",
889 "tracing",
890 ]
891
892 [[package]]
820 893 name = "giterated-protocol"
821 894 version = "0.1.0"
822 895 dependencies = [

Cargo.toml

View file
@@ -4,5 +4,9 @@ members = [
4 4 "giterated-models",
5 5 "giterated-stack",
6 6 "giterated-cache",
7 "giterated-protocol"
7 "giterated-protocol",
8 "giterated-plugins/giterated-plugin",
9 "giterated-plugins/giterated-plugin-sys",
10 "giterated-plugins/example-plugin",
11 "giterated-plugins/giterated-issues"
8 12 ]
8 12 \ No newline at end of file

giterated-daemon/migrations/20231104175218_create_issues.sql

View file
@@ -0,0 +1,25 @@
1 CREATE TYPE comment_visibility AS ENUM
2 (
3 'public',
4 'maintainers',
5 'private'
6 );
7
8 CREATE TABLE IF NOT EXISTS issues
9 (
10 id SERIAL PRIMARY KEY,
11 repository TEXT NOT NULL,
12 author TEXT NOT NULL,
13 creation_date INTEGER NOT NULL,
14 issue_name TEXT NOT NULL,
15 contents TEXT
16 );
17
18 CREATE TABLE IF NOT EXISTS issue_comments
19 (
20 id SERIAL PRIMARY KEY,
21 issue INTEGER NOT NULL REFERENCES issues(id),
22 author TEXT NOT NULL,
23 visibility comment_visibility NOT NULL,
24 contents TEXT
25 );
25 \ No newline at end of file

giterated-daemon/src/database_backend/mod.rs

View file
@@ -7,7 +7,9 @@ use std::sync::Arc;
7 7 use anyhow::Context;
8 8
9 9 use giterated_models::instance::Instance;
10 use giterated_models::repository::{DefaultBranch, Description, Repository, Visibility, CommitBodyType};
10 use giterated_models::repository::{
11 CommitBodyType, DefaultBranch, Description, Repository, Visibility,
12 };
11 13 use giterated_models::user::{Bio, DisplayName, User};
12 14 use giterated_stack::provider::MetadataProvider;
13 15 use giterated_stack::{AnyObject, AnySetting, GiteratedStack, ObjectMeta, SubstackBuilder};

giterated-models/src/object/operations.rs

View file
@@ -16,6 +16,10 @@ impl GiteratedOperation<Instance> for ObjectRequest {
16 16 type Success = ObjectResponse;
17 17
18 18 type Failure = ObjectRequestError;
19
20 fn operation_name() -> &'static str {
21 "object_request"
22 }
19 23 }
20 24
21 25 #[derive(Debug, Clone, thiserror::Error, Serialize, Deserialize)]

giterated-models/src/repository/mod.rs

View file
@@ -69,11 +69,17 @@ impl GiteratedObject for Repository {
69 69 }
70 70 }
71 71
72 impl TryFrom<String> for Repository {
73 type Error = RepositoryParseError;
72 // impl TryFrom<String> for Repository {
73 // type Error = RepositoryParseError;
74 74
75 fn try_from(value: String) -> Result<Self, Self::Error> {
76 Self::from_str(&value)
75 // fn try_from(value: String) -> Result<Self, Self::Error> {
76 // Self::from_str(&value)
77 // }
78 // }
79
80 impl From<String> for Repository {
81 fn from(value: String) -> Self {
82 Repository::from_str(&value).unwrap()
77 83 }
78 84 }
79 85

giterated-models/src/repository/settings.rs

View file
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
2 2
3 3 use crate::{settings::Setting, user::User};
4 4
5 use super::{DefaultBranch, CommitBodyType};
5 use super::{CommitBodyType, DefaultBranch};
6 6
7 7 impl Setting for DefaultBranch {
8 8 fn name() -> &'static str {

giterated-models/src/repository/values.rs

View file
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
4 4
5 5 use crate::{settings::Setting, value::GiteratedObjectValue};
6 6
7 use super::{Commit, Repository, RepositoryVisibility, CommitBodyType};
7 use super::{Commit, CommitBodyType, Repository, RepositoryVisibility};
8 8
9 9 // pub struct RepositorySetting<V: GiteratedObjectValue>(pub V);
10 10

giterated-plugins/README.md

View file
@@ -0,0 +1,3 @@
1 # Plugins
2
3 Giterated uses a Plugin system to allow for easy extension, but also to separate concerns in the core.
3 \ No newline at end of file

giterated-plugins/example-plugin/Cargo.toml

View file
@@ -0,0 +1,21 @@
1 [package]
2 name = "example-plugin"
3 version = "0.1.0"
4 edition = "2021"
5
6 [lib]
7 name = "example_plugin_dylib"
8 path = "src/lib.rs"
9 crate-type = ["dylib"]
10
11 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12
13 [dependencies]
14 giterated-plugin = { path = "../giterated-plugin" }
15 giterated-plugin-sys = { path = "../giterated-plugin-sys" }
16 dlopen2 = "0.6"
17 tracing-subscriber = "0.3"
18 giterated-models = { path = "../../giterated-models" }
19 tracing = "0.1"
20 serde_json = "1.0"
21 anyhow = "1"

giterated-plugins/example-plugin/src/lib.rs

View file
@@ -0,0 +1,95 @@
1 use std::sync::OnceLock;
2
3 use giterated_models::{
4 error::OperationError,
5 instance::Instance,
6 object::ObjectRequest,
7 user::{DisplayName, User},
8 };
9 use giterated_plugin::{
10 handle::PluginInitializationState,
11 new_stack::{FFIPluginMeta, PluginState},
12 HostVTable, InitializationVTable,
13 };
14 use giterated_plugin_sys::PluginStackBuilder;
15 use tracing::{info, trace_span, Level};
16
17 static INIT_VTABLE: OnceLock<InitializationVTable> = OnceLock::new();
18
19 #[no_mangle]
20 pub extern "C" fn plugin_meta() -> FFIPluginMeta {
21 const PLUGIN_NAME: &str = "Example Plugin";
22 const PLUGIN_VERSION: &str = "1.0.0";
23
24 FFIPluginMeta {
25 name: PLUGIN_NAME.as_ptr(),
26 name_len: PLUGIN_NAME.len(),
27 version: PLUGIN_VERSION.as_ptr(),
28 version_len: PLUGIN_VERSION.len(),
29 }
30 }
31
32 #[no_mangle]
33 pub extern "C" fn load_host_vtable(_vtable: &HostVTable) {
34 println!("Loading vtable");
35 }
36
37 #[no_mangle]
38 pub extern "C" fn load_initialization_vtable(init_vtable: &InitializationVTable) {
39 INIT_VTABLE.set(init_vtable.clone()).unwrap();
40 println!("Loaded initialization vtable");
41 }
42
43 #[no_mangle]
44 pub extern "C" fn initialize() -> PluginState {
45 tracing_subscriber::fmt()
46 .pretty()
47 .with_thread_names(true)
48 .with_max_level(Level::TRACE)
49 .init();
50
51 PluginState {
52 inner: Box::into_raw(Box::new(())),
53 }
54 }
55
56 #[no_mangle]
57 pub extern "C" fn initialize_registration(
58 state: *mut PluginInitializationState,
59 ) -> *mut PluginInitializationState {
60 let _guard: tracing::span::EnteredSpan = trace_span!("initialize_registration").entered();
61 let init_vtable = INIT_VTABLE.get().unwrap();
62 let mut builder = PluginStackBuilder::new((), state, init_vtable);
63
64 builder.object::<Instance>().object::<User>();
65 builder.operation_handler(handler);
66 builder.value(value_getter);
67 // builder.setting_getter(setting_getter);
68
69 state
70 }
71
72 async fn handler(
73 state: (),
74 _object: Instance,
75 _operation: ObjectRequest,
76 ) -> Result<(), OperationError<()>> {
77 info!("handling operation!");
78
79 todo!()
80 }
81
82 async fn value_getter(
83 state: (),
84 object: User,
85 ) -> Result<DisplayName, OperationError<anyhow::Error>> {
86 info!("OwO, value gotten!");
87
88 Ok(DisplayName(String::from("heya!")))
89 }
90
91 // fn setting_getter(state: (), object: User) -> Result<DisplayName, ()> {
92 // info!("OwO, setting gotten!");
93
94 // Ok(DisplayName(String::from("heya! (but from a setting)")))
95 // }

giterated-plugins/example-plugin/src/main.rs

View file
@@ -0,0 +1,63 @@
1 use dlopen2::wrapper::Container;
2 use giterated_models::{
3 instance::Instance,
4 object::{GiteratedObject, ObjectRequest},
5 operation::GiteratedOperation,
6 settings::{GetSetting, Setting},
7 user::{DisplayName, User},
8 value::GetValue,
9 };
10 use giterated_plugin::{handle::PluginHandle, new_stack::Runtime, GiteratedPluginApi};
11 use tracing::{info, Level};
12
13 fn main() {
14 tracing_subscriber::fmt()
15 .pretty()
16 .with_thread_names(true)
17 .with_max_level(Level::TRACE)
18 .init();
19
20 let mut handle = PluginHandle::from_dylib("example_plugin_dylib.dll").unwrap();
21
22 let mut runtime = Runtime::default();
23
24 runtime.insert_plugin(handle);
25
26 let object_request = ObjectRequest(String::from("foobar"));
27
28 match runtime.handle(
29 Instance::object_name(),
30 ObjectRequest::operation_name(),
31 "meow",
32 &serde_json::to_vec(&object_request).unwrap(),
33 ) {
34 Ok(success) => info!("handler call success!"),
35 Err(_) => info!("handler call error"),
36 }
37
38 match runtime.handle(
39 User::object_name(),
40 <GetValue as GiteratedOperation<User>>::operation_name(),
41 "amber:giterated.dev",
42 &serde_json::to_vec(&GetValue {
43 value_name: String::from("display_name"),
44 })
45 .unwrap(),
46 ) {
47 Ok(success) => info!("get_value handler call success!"),
48 Err(_) => info!("get_value handler call error"),
49 }
50
51 match runtime.handle(
52 User::object_name(),
53 <GetSetting as GiteratedOperation<User>>::operation_name(),
54 "amber:giterated.dev",
55 &serde_json::to_vec(&GetSetting {
56 setting_name: DisplayName::name().to_string(),
57 })
58 .unwrap(),
59 ) {
60 Ok(success) => info!("get_setting handler call success!"),
61 Err(_) => info!("get_setting handler call error"),
62 }
63 }

giterated-plugins/giterated-issues/Cargo.toml

View file
@@ -0,0 +1,16 @@
1 [package]
2 name = "giterated-issues"
3 version = "0.1.0"
4 edition = "2021"
5
6 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
8 [dependencies]
9 giterated-plugin = { path = "../giterated-plugin" }
10 giterated-models = { path = "../../giterated-models" }
11 serde = { version = "1.0", features = [ "derive" ]}
12 anyhow = "1"
13 thiserror = "1"
14 sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native-tls", "postgres", "macros", "migrate", "chrono" ] }
15 tokio = { version = "1.32", features = [ "full" ] }
16 giterated-plugin-sys = { path = "../giterated-plugin-sys" }

giterated-plugins/giterated-issues/src/db.rs

View file
@@ -0,0 +1,24 @@
1 use giterated_models::{repository::Repository, user::User};
2
3 use crate::operations::CommentVisibility;
4
5 /// An [`Issue`]'s database representation.
6 #[derive(Debug, sqlx::FromRow)]
7 pub struct IssueRow {
8 pub id: i32,
9 #[sqlx(try_from = "String")]
10 pub repository: Repository,
11 #[sqlx(try_from = "String")]
12 pub author: User,
13 pub creation_date: i32,
14 pub issue_name: String,
15 pub contents: Option<String>,
16 }
17
18 #[derive(Debug, sqlx::FromRow)]
19 pub struct IssueCommentRow {
20 #[sqlx(try_from = "String")]
21 pub author: User,
22 pub contents: Option<String>,
23 pub visibility: CommentVisibility,
24 }

giterated-plugins/giterated-issues/src/handlers.rs

View file
@@ -0,0 +1,118 @@
1 use crate::db::IssueRow;
2 use crate::setting::Contents;
3 use crate::value::{Author, CommentCount, CreationDate, Name};
4 use crate::IssuesPluginState;
5 use crate::{
6 operations::{
7 CreateIssueRequest, IssueCreationError, IssueEditError, IssueEditRequest,
8 IssuePostCommentError, IssuePostCommentRequest, IssueQueryError, QueryIssuesRequest,
9 },
10 Issue,
11 };
12 use giterated_models::error::IntoInternalError;
13 use giterated_models::user::User;
14 use giterated_models::{error::OperationError, repository::Repository};
15 use sqlx::PgPool;
16
17 pub async fn create_issue_request(
18 state: IssuesPluginState,
19 repository: Repository,
20 request: CreateIssueRequest,
21 ) -> Result<Issue, OperationError<IssueCreationError>> {
22 // TODO: AUTHN & AUTHZ
23 let issue = sqlx::query_as!(
24 IssueRow,
25 r#"INSERT INTO issues VALUES (null, $1, $2, $3, $4, $5) RETURNING *"#,
26 repository.to_string(),
27 request.author.to_string(),
28 0,
29 request.name,
30 request.contents,
31 )
32 .fetch_one(&state.pool)
33 .await
34 .as_internal_error_with_context("creating issue in db")?;
35
36 Ok(Issue {
37 repository,
38 id: issue.id as u32,
39 })
40 }
41
42 pub async fn query_issues_request(
43 state: IssuesPluginState,
44 repository: Repository,
45 request: QueryIssuesRequest,
46 ) -> Result<Vec<Issue>, OperationError<IssueQueryError>> {
47 // TODO: AUTHN & AUTHZ
48 todo!()
49 }
50
51 pub async fn edit_issue_request(
52 state: IssuesPluginState,
53 issue: Issue,
54 request: IssueEditRequest,
55 ) -> Result<(), OperationError<IssueEditError>> {
56 // TODO: AUTHN & AUTHZ
57 todo!()
58 }
59
60 pub async fn issue_post_comment_request(
61 state: IssuesPluginState,
62 issue: Issue,
63 request: IssuePostCommentRequest,
64 ) -> Result<u32, OperationError<IssuePostCommentError>> {
65 // TODO: AUTHN & AUTHZ
66 todo!()
67 }
68
69 pub async fn issue_value_author(
70 state: IssuesPluginState,
71 issue: Issue,
72 ) -> Result<Author, OperationError<anyhow::Error>> {
73 todo!()
74 }
75
76 pub async fn issue_value_creation_date(
77 state: IssuesPluginState,
78 issue: Issue,
79 ) -> Result<CreationDate, OperationError<anyhow::Error>> {
80 todo!()
81 }
82
83 pub async fn issue_value_comment_count(
84 state: IssuesPluginState,
85 issue: Issue,
86 ) -> Result<CommentCount, OperationError<anyhow::Error>> {
87 todo!()
88 }
89
90 pub async fn issue_set_setting_name(
91 state: IssuesPluginState,
92 issue: Issue,
93 name: Name,
94 ) -> Result<(), OperationError<anyhow::Error>> {
95 todo!()
96 }
97
98 pub async fn issue_get_setting_name(
99 state: IssuesPluginState,
100 issue: Issue,
101 ) -> Result<Name, OperationError<anyhow::Error>> {
102 todo!()
103 }
104
105 pub async fn issue_set_setting_contents(
106 state: IssuesPluginState,
107 issue: Issue,
108 contents: Contents,
109 ) -> Result<(), OperationError<anyhow::Error>> {
110 todo!()
111 }
112
113 pub async fn issue_get_setting_contents(
114 state: IssuesPluginState,
115 issue: Issue,
116 ) -> Result<Contents, OperationError<anyhow::Error>> {
117 todo!()
118 }

giterated-plugins/giterated-issues/src/lib.rs

View file
@@ -0,0 +1,152 @@
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 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::{Contents, NotificationsOverride};
18 use sqlx::PgPool;
19 use value::{Author, CommentCount, CreationDate, Name};
20
21 pub mod db;
22 pub mod handlers;
23 pub mod operations;
24 pub mod setting;
25 pub mod value;
26
27 /// An issue, defined by the repository which owns it and its index.
28 ///
29 /// # Textual Format
30 /// An issue's textual format is defined as:
31 ///
32 /// `@{index: u32}:{repository: Repository}`
33 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
34 pub struct Issue {
35 pub repository: Repository,
36 pub id: u32,
37 }
38
39 impl Display for Issue {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.write_str(&format!("{}:{}", self.id, self.repository))
42 }
43 }
44
45 impl GiteratedObject for Issue {
46 fn object_name() -> &'static str {
47 "issue"
48 }
49
50 fn home_uri(&self) -> String {
51 self.repository.home_uri()
52 }
53
54 fn from_object_str(object_str: &str) -> Result<Self, Error> {
55 Ok(Issue::from_str(object_str)?)
56 }
57 }
58
59 impl FromStr for Issue {
60 type Err = IssueParseError;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 let (index, repository) = s.split_once(':').ok_or_else(|| IssueParseError)?;
64
65 let id: u32 = index.parse().map_err(|_| IssueParseError)?;
66 let repository = Repository::from_str(repository).map_err(|_| IssueParseError)?;
67
68 Ok(Self { repository, id })
69 }
70 }
71
72 #[derive(Debug, thiserror::Error)]
73 #[error("error parsing issue")]
74 pub struct IssueParseError;
75
76 static INIT_VTABLE: OnceLock<InitializationVTable> = OnceLock::new();
77
78 #[no_mangle]
79 pub extern "C" fn plugin_meta() -> FFIPluginMeta {
80 const PLUGIN_NAME: &str = "Giterated [Issues]";
81 const PLUGIN_VERSION: &str = "0.1.0";
82
83 FFIPluginMeta {
84 name: PLUGIN_NAME.as_ptr(),
85 name_len: PLUGIN_NAME.len(),
86 version: PLUGIN_VERSION.as_ptr(),
87 version_len: PLUGIN_VERSION.len(),
88 }
89 }
90
91 #[no_mangle]
92 pub extern "C" fn load_host_vtable(_vtable: &HostVTable) {
93 println!("Loading vtable");
94 }
95
96 #[no_mangle]
97 pub extern "C" fn load_initialization_vtable(init_vtable: &InitializationVTable) {
98 INIT_VTABLE.set(init_vtable.clone()).unwrap();
99 println!("Loaded initialization vtable");
100 }
101
102 #[no_mangle]
103 pub extern "C" fn initialize() -> PluginState {
104 // tracing_subscriber::fmt()
105 // .pretty()
106 // .with_thread_names(true)
107 // .with_max_level(Level::TRACE)
108 // .init();
109
110 PluginState {
111 inner: Box::into_raw(Box::new(())),
112 }
113 }
114
115 #[no_mangle]
116 pub extern "C" fn initialize_registration(
117 state: *mut PluginInitializationState,
118 ) -> *mut PluginInitializationState {
119 // let _guard: tracing::span::EnteredSpan = trace_span!("initialize_registration").entered();
120 let init_vtable = INIT_VTABLE.get().unwrap();
121
122 let plugin_state = { todo!() };
123
124 let mut builder = PluginStackBuilder::new(plugin_state, state, init_vtable);
125
126 builder.object::<Issue>();
127
128 builder
129 .object_setting(issue_get_setting_name, issue_set_setting_name)
130 .object_setting(issue_get_setting_contents, issue_set_setting_contents);
131
132 builder.object_user_setting::<Issue, NotificationsOverride>();
133
134 builder
135 .value(issue_value_creation_date)
136 .value(issue_value_comment_count)
137 .value(issue_get_setting_name)
138 .value(issue_value_author);
139
140 builder
141 .operation_handler(create_issue_request)
142 .operation_handler(query_issues_request)
143 .operation_handler(edit_issue_request)
144 .operation_handler(issue_post_comment_request);
145
146 state
147 }
148
149 #[derive(Clone, Debug)]
150 pub struct IssuesPluginState {
151 pub pool: PgPool,
152 }

giterated-plugins/giterated-issues/src/operations.rs

View file
@@ -0,0 +1,75 @@
1 use giterated_models::{operation::GiteratedOperation, repository::Repository, user::User};
2 use serde::{Deserialize, Serialize};
3
4 use crate::Issue;
5
6 /// Create an [`Issue`] on a [`Repository`].
7 #[derive(Clone, Debug, Serialize, Deserialize)]
8 pub struct CreateIssueRequest {
9 pub name: String,
10 pub contents: String,
11 pub author: User,
12 }
13
14 impl GiteratedOperation<Repository> for CreateIssueRequest {
15 type Success = Issue;
16
17 type Failure = IssueCreationError;
18 }
19
20 #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
21 #[error("failed to create issue")]
22 pub struct IssueCreationError;
23
24 /// Query for [`Issue`]s on a [`Repository`].
25 #[derive(Clone, Debug, Serialize, Deserialize)]
26 pub struct QueryIssuesRequest {}
27
28 impl GiteratedOperation<Repository> for QueryIssuesRequest {
29 type Success = Vec<Issue>;
30
31 type Failure = IssueQueryError;
32 }
33
34 #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
35 #[error("failed to query issues")]
36 pub struct IssueQueryError;
37
38 #[derive(Clone, Debug, Serialize, Deserialize)]
39 pub struct IssueEditRequest {
40 // Might not be needed :)
41 }
42
43 impl GiteratedOperation<Issue> for IssueEditRequest {
44 type Success = ();
45
46 type Failure = IssueEditError;
47 }
48
49 #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
50 #[error("failed to edit issue")]
51 pub struct IssueEditError;
52
53 #[derive(Clone, Debug, Serialize, Deserialize)]
54 pub struct IssuePostCommentRequest {
55 pub contents: String,
56 pub visibility: CommentVisibility,
57 }
58
59 impl GiteratedOperation<Issue> for IssuePostCommentRequest {
60 type Success = u32;
61
62 type Failure = IssuePostCommentError;
63 }
64
65 #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
66 #[error("failed to post comment")]
67 pub struct IssuePostCommentError;
68
69 #[derive(PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Clone, sqlx::Type)]
70 #[sqlx(type_name = "comment_visibility", rename_all = "lowercase")]
71 pub enum CommentVisibility {
72 Public,
73 Maintainers,
74 Private,
75 }

giterated-plugins/giterated-issues/src/setting.rs

View file
@@ -0,0 +1,28 @@
1 use giterated_models::settings::Setting;
2 use serde::{Deserialize, Serialize};
3
4 use crate::value::Name;
5
6 impl Setting for Name {
7 fn name() -> &'static str {
8 "name"
9 }
10 }
11
12 #[derive(Clone, Debug, Serialize, Deserialize)]
13 pub struct Contents(String);
14
15 impl Setting for Contents {
16 fn name() -> &'static str {
17 "contents"
18 }
19 }
20
21 #[derive(Clone, Debug, Serialize, Deserialize)]
22 pub struct NotificationsOverride(pub bool);
23
24 impl Setting for NotificationsOverride {
25 fn name() -> &'static str {
26 "notifications_override"
27 }
28 }

giterated-plugins/giterated-issues/src/value.rs

View file
@@ -0,0 +1,48 @@
1 use giterated_models::{user::User, value::GiteratedObjectValue};
2 use serde::{Deserialize, Serialize};
3
4 use crate::Issue;
5
6 #[derive(Clone, Debug, Serialize, Deserialize)]
7 pub struct CreationDate(pub String);
8
9 impl GiteratedObjectValue for CreationDate {
10 type Object = Issue;
11
12 fn value_name() -> &'static str {
13 "creation_date"
14 }
15 }
16
17 #[derive(Clone, Debug, Serialize, Deserialize)]
18 pub struct CommentCount(pub u32);
19
20 impl GiteratedObjectValue for CommentCount {
21 type Object = Issue;
22
23 fn value_name() -> &'static str {
24 "comment_count"
25 }
26 }
27
28 #[derive(Clone, Debug, Serialize, Deserialize)]
29 pub struct Name(pub String);
30
31 impl GiteratedObjectValue for Name {
32 type Object = Issue;
33
34 fn value_name() -> &'static str {
35 "name"
36 }
37 }
38
39 #[derive(Clone, Debug, Serialize, Deserialize)]
40 pub struct Author(pub User);
41
42 impl GiteratedObjectValue for Author {
43 type Object = Issue;
44
45 fn value_name() -> &'static str {
46 "owner"
47 }
48 }

giterated-plugins/giterated-plugin-sys/Cargo.toml

View file
@@ -0,0 +1,11 @@
1 [package]
2 name = "giterated-plugin-sys"
3 version = "0.1.0"
4 edition = "2021"
5
6 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
8 [dependencies]
9 giterated-plugin = { path = "../giterated-plugin" }
10 tracing = "0.1"
11 giterated-models = { path = "../../giterated-models" }
11 \ No newline at end of file

giterated-plugins/giterated-plugin-sys/src/lib.rs

View file
@@ -0,0 +1,211 @@
1 use std::sync::OnceLock;
2
3 use giterated_models::{
4 object::GiteratedObject, operation::GiteratedOperation, settings::Setting,
5 value::GiteratedObjectValue,
6 };
7 use giterated_plugin::{
8 callback::{
9 IntoPluginOperationHandler, IntoPluginSettingGetter, IntoPluginSettingSetter,
10 IntoPluginValueGetter, OperationHandlerCallback, SettingGetterCallback,
11 ValueGetterCallback,
12 },
13 handle::PluginInitializationState,
14 InitializationVTable, IntoObjectVTable, IntoOperationVTable, IntoSettingVTable,
15 IntoValueVTable, ObjectVtable, OperationVTable, SettingVtable, ValueVTable,
16 };
17 use tracing::trace_span;
18
19 pub struct PluginStackBuilder<'init, S> {
20 init_state: *mut PluginInitializationState,
21 vtable: &'init InitializationVTable,
22 state: S,
23 }
24
25 impl<'init, S> PluginStackBuilder<'init, S> {
26 pub fn new(
27 plugin_state: S,
28 state: *mut PluginInitializationState,
29 vtable: &'init InitializationVTable,
30 ) -> Self {
31 Self {
32 init_state: state,
33 vtable,
34 state: plugin_state,
35 }
36 }
37
38 pub fn object<O: IntoObjectVTable + GiteratedObject>(&mut self) -> &mut Self {
39 let _guard = trace_span!("register object").entered();
40
41 let func = self.vtable.register_object;
42
43 unsafe { func(self.init_state, O::object_name(), ObjectVtable::new::<O>()) };
44
45 self
46 }
47
48 pub fn operation<O, D>(&mut self) -> &mut Self
49 where
50 D: IntoOperationVTable<O> + GiteratedOperation<O>,
51 O: GiteratedObject,
52 {
53 let _guard = trace_span!("register operation").entered();
54
55 unsafe {
56 (self.vtable.register_operation)(
57 self.init_state,
58 O::object_name(),
59 D::operation_name(),
60 OperationVTable::new::<O, D>(),
61 )
62 }
63
64 self
65 }
66
67 pub fn object_setting<O, OS, HG, HS>(&mut self, get: HG, set: HS) -> &mut Self
68 where
69 O: GiteratedObject,
70 OS: IntoSettingVTable + Setting,
71 HG: IntoPluginSettingGetter<S, O, OS>,
72 HS: IntoPluginSettingSetter<S, O, OS>,
73 {
74 let _guard = trace_span!("register setting").entered();
75
76 unsafe {
77 (self.vtable.register_setting)(
78 self.init_state,
79 O::object_name(),
80 OS::name(),
81 SettingVtable::new::<OS>(),
82 )
83 }
84
85 self
86 }
87
88 pub fn object_user_setting<O, OS>(&mut self) -> &mut Self
89 where
90 O: GiteratedObject,
91 OS: IntoSettingVTable + Setting,
92 {
93 let _guard = trace_span!("register setting").entered();
94
95 unsafe {
96 (self.vtable.register_setting)(
97 self.init_state,
98 O::object_name(),
99 OS::name(),
100 SettingVtable::new::<OS>(),
101 )
102 }
103
104 self
105 }
106
107 pub fn value<O, V, T>(&mut self, handler: T) -> &mut Self
108 where
109 O: GiteratedObject,
110 V: IntoValueVTable<O> + GiteratedObjectValue<Object = O>,
111 T: IntoPluginValueGetter<S, O, V>,
112 {
113 let _guard = trace_span!("register value").entered();
114
115 unsafe {
116 (self.vtable.register_value)(
117 self.init_state,
118 O::object_name(),
119 V::value_name(),
120 ValueVTable::new::<O, V>(),
121 )
122 }
123
124 unsafe {
125 (self.vtable.value_getter)(
126 self.init_state,
127 O::object_name(),
128 V::value_name(),
129 ValueGetterCallback::new::<S, O, V, T>(handler),
130 )
131 }
132
133 self
134 }
135
136 pub fn operation_handler<
137 DS,
138 DF,
139 O: GiteratedObject + IntoObjectVTable,
140 D: IntoOperationVTable<O> + GiteratedOperation<O>,
141 T: IntoPluginOperationHandler<S, O, D, DS, DF>,
142 >(
143 &mut self,
144 handler: T,
145 ) -> &mut Self {
146 let _guard = trace_span!("register operation handler").entered();
147
148 unsafe {
149 (self.vtable.operation_handler)(
150 self.init_state,
151 O::object_name(),
152 D::operation_name(),
153 OperationHandlerCallback::new::<S, O, D, DS, DF, T>(handler),
154 )
155 }
156
157 // TODO: Yikes?
158 self.object::<O>();
159
160 self.operation::<O, D>();
161
162 self
163 }
164
165 // pub fn value_getter<O, V, T>(&mut self, handler: T) -> &mut Self
166 // where
167 // O: GiteratedObject + IntoObjectVTable,
168 // V: GiteratedObjectValue<Object = O> + IntoValueVTable<O>,
169 // T: IntoPluginValueGetter<S, O, V>,
170 // {
171 // let _guard = trace_span!("register value_getter handler").entered();
172
173 // unsafe {
174 // (self.vtable.value_getter)(
175 // self.init_state,
176 // O::object_name(),
177 // V::value_name(),
178 // ValueGetterCallback::new::<S, O, V, T>(handler),
179 // )
180 // }
181
182 // // TODO: Yikes?
183 // self.object::<O>();
184 // self.value::<O, V>();
185
186 // self
187 // }
188
189 // pub fn setting_getter<O, OS, T>(&mut self, handler: T) -> &mut Self
190 // where
191 // O: GiteratedObject + IntoObjectVTable,
192 // OS: Setting + IntoSettingVTable,
193 // T: IntoPluginSettingGetter<S, O, OS>,
194 // {
195 // let _guard = trace_span!("register setting_getter handler").entered();
196
197 // unsafe {
198 // (self.vtable.setting_getter)(
199 // self.init_state,
200 // O::object_name(),
201 // OS::name(),
202 // SettingGetterCallback::new::<S, O, OS, T>(handler),
203 // )
204 // }
205
206 // self.object::<O>();
207 // // self.setting::<O, OS>();
208
209 // self
210 // }
211 }

giterated-plugins/giterated-plugin/Cargo.toml

View file
@@ -0,0 +1,15 @@
1 [package]
2 name = "giterated-plugin"
3 version = "0.1.0"
4 edition = "2021"
5
6 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
8 [dependencies]
9 dlopen2 = "0.6"
10 anyhow = "1"
11 thiserror = "1"
12 tracing = "0.1"
13 giterated-models = { path = "../../giterated-models" }
14 semver = "*"
15 serde_json = "1.0"
15 \ No newline at end of file

giterated-plugins/giterated-plugin/src/callback/mod.rs

View file
@@ -0,0 +1,18 @@
1 mod operation;
2 pub use operation::*;
3 mod value;
4 pub use value::*;
5 mod setting;
6 pub use setting::*;
7
8 /// A container for a callback pointer, used to provide an internal callback function or
9 /// state to a plugin when performing a callback.
10 #[derive(Clone, Copy)]
11 #[repr(C)]
12 pub struct CallbackPtr(*const ());
13
14 impl CallbackPtr {
15 pub unsafe fn from_raw(callback: *const ()) -> Self {
16 Self(callback)
17 }
18 }

giterated-plugins/giterated-plugin/src/callback/operation.rs

View file
@@ -0,0 +1,75 @@
1 use giterated_models::error::OperationError;
2
3 use crate::{new_stack::PluginState, AnyObject, AnyOperation};
4
5 use std::{any::type_name, fmt::Debug, future::Future};
6
7 use super::CallbackPtr;
8
9 #[derive(Clone, Copy)]
10 pub struct OperationHandlerCallback {
11 pub callback_ptr: CallbackPtr,
12 pub func:
13 unsafe extern "C" fn(CallbackPtr, &PluginState, object: AnyObject, operation: AnyOperation),
14 }
15
16 impl OperationHandlerCallback {
17 pub fn new<S, O, D, DS, DF, T: IntoPluginOperationHandler<S, O, D, DS, DF>>(
18 handler: T,
19 ) -> Self {
20 OperationHandlerCallback {
21 func: T::handle,
22 callback_ptr: T::callback_ptr(&handler),
23 }
24 }
25 }
26
27 pub trait IntoPluginOperationHandler<S, O, D, DS, DF> {
28 unsafe extern "C" fn handle(
29 callback_ptr: CallbackPtr,
30 state: &PluginState,
31 object: AnyObject,
32 operation: AnyOperation,
33 );
34 fn callback_ptr(&self) -> CallbackPtr;
35 }
36
37 impl<F, S, O, D, DS, DF, Fut> IntoPluginOperationHandler<S, O, D, DS, DF> for F
38 where
39 Fut: Future<Output = Result<DS, OperationError<DF>>>,
40 F: Fn(S, O, D) -> Fut,
41 S: Clone + Debug,
42 O: Debug,
43 D: Debug,
44 {
45 unsafe extern "C" fn handle(
46 callback: CallbackPtr,
47 state: &PluginState,
48 mut object: AnyObject,
49 mut operation: AnyOperation,
50 ) {
51 let _guard = trace_span!(
52 "operation handler",
53 object = type_name::<O>(),
54 operation = type_name::<D>()
55 )
56 .entered();
57 let state = unsafe { state.transmute_ref::<S>() };
58
59 // Since this is Rust code, we know that the AnyObject and AnyOperation are just boxes
60 let object = unsafe { object.transmute_owned::<O>() };
61 let operation = unsafe { operation.transmute_owned::<D>() };
62
63 // Cast the callback ptr to ourselves
64 let callback: *const F = std::mem::transmute(callback.0);
65 let callback = callback.as_ref().unwrap();
66
67 // callback(state.clone(), *object, *operation)
68
69 todo!()
70 }
71
72 fn callback_ptr(&self) -> CallbackPtr {
73 unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) }
74 }
75 }

giterated-plugins/giterated-plugin/src/callback/setting.rs

View file
@@ -0,0 +1,168 @@
1 use std::future::Future;
2
3 use giterated_models::{
4 error::OperationError,
5 object::GiteratedObject,
6 settings::{AnySetting, Setting},
7 };
8
9 use crate::{new_stack::PluginState, AnyObject, NewAnySetting};
10
11 use super::CallbackPtr;
12
13 #[derive(Clone, Copy)]
14 pub struct SettingGetterCallback {
15 pub callback_ptr: CallbackPtr,
16 pub func: unsafe extern "C" fn(
17 CallbackPtr,
18 &PluginState,
19 object: AnyObject,
20 ) -> Result<NewAnySetting, ()>,
21 }
22
23 impl SettingGetterCallback {
24 pub fn new<S, O, OS, T: IntoPluginSettingGetter<S, O, OS>>(callback: T) -> Self {
25 Self {
26 func: T::get_setting,
27 callback_ptr: callback.callback_ptr(),
28 }
29 }
30 }
31
32 pub trait IntoPluginSettingGetter<S, O, OS> {
33 unsafe extern "C" fn get_setting(
34 callback_ptr: CallbackPtr,
35 state: &PluginState,
36 object: AnyObject,
37 ) -> Result<NewAnySetting, ()>;
38
39 fn callback_ptr(&self) -> CallbackPtr {
40 unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) }
41 }
42 }
43
44 impl<F, S, O, OS, Fut> IntoPluginSettingGetter<S, O, OS> for F
45 where
46 Fut: Future<Output = Result<OS, OperationError<anyhow::Error>>>,
47 S: Clone,
48 O: GiteratedObject,
49 OS: Setting,
50 F: Fn(S, O) -> Fut,
51 {
52 unsafe extern "C" fn get_setting(
53 callback: CallbackPtr,
54 state: &PluginState,
55 mut object: AnyObject,
56 ) -> Result<NewAnySetting, ()> {
57 let _guard = trace_span!(
58 "get_setting handler",
59 object = O::object_name(),
60 setting = OS::name()
61 )
62 .entered();
63 let state = unsafe { state.transmute_ref::<S>() };
64
65 let object = unsafe { object.transmute_owned::<O>() };
66
67 // Cast the callback ptr to ourselves
68 let callback: *const F = std::mem::transmute(callback.0);
69 let callback = callback.as_ref().unwrap();
70
71 // let result = callback(state.clone(), *object);
72
73 // match result {
74 // Ok(setting) => Ok(NewAnySetting::new(setting)),
75 // Err(_) => todo!(),
76 // }
77
78 todo!()
79 }
80 }
81
82 pub trait IntoPluginSettingSetter<S, O, OS> {
83 unsafe extern "C" fn set_setting(
84 callback_ptr: CallbackPtr,
85 state: &PluginState,
86 object: AnyObject,
87 setting: AnySetting,
88 ) -> Result<(), ()>;
89
90 fn callback_ptr(&self) -> CallbackPtr {
91 unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) }
92 }
93 }
94
95 impl<F, S, O, OS, Fut> IntoPluginSettingSetter<S, O, OS> for F
96 where
97 Fut: Future<Output = Result<(), OperationError<anyhow::Error>>>,
98 S: Clone,
99 O: GiteratedObject,
100 OS: Setting,
101 F: Fn(S, O, OS) -> Fut,
102 {
103 unsafe extern "C" fn set_setting(
104 callback: CallbackPtr,
105 state: &PluginState,
106 mut object: AnyObject,
107 setting: AnySetting,
108 ) -> Result<(), ()> {
109 let _guard = trace_span!(
110 "get_setting handler",
111 object = O::object_name(),
112 setting = OS::name()
113 )
114 .entered();
115 let state = unsafe { state.transmute_ref::<S>() };
116
117 let object = unsafe { object.transmute_owned::<O>() };
118
119 // Cast the callback ptr to ourselves
120 let callback: *const F = std::mem::transmute(callback.0);
121 let callback = callback.as_ref().unwrap();
122
123 // let result = callback(state.clone(), *object);
124
125 // match result {
126 // Ok(setting) => Ok(NewAnySetting::new(setting)),
127 // Err(_) => todo!(),
128 // }
129 todo!()
130 }
131 }
132
133 pub struct SettingChangeCallback {
134 func: unsafe extern "C" fn(
135 &PluginState,
136 object: AnyObject,
137 setting_name: &str,
138 new_setting: NewAnySetting,
139 ),
140 }
141
142 pub trait IntoSettingChangeCallback<S, O> {
143 unsafe extern "C" fn setting_changed(
144 state: &PluginState,
145 object: AnyObject,
146 setting_name: &str,
147 new_setting: NewAnySetting,
148 );
149 }
150
151 impl<F, S, O> IntoSettingChangeCallback<S, O> for F {
152 unsafe extern "C" fn setting_changed(
153 state: &PluginState,
154 object: AnyObject,
155 setting_name: &str,
156 new_setting: NewAnySetting,
157 ) {
158 todo!()
159 }
160 }
161
162 impl SettingChangeCallback {
163 pub fn new<S, O, T: IntoSettingChangeCallback<S, O>>() -> Self {
164 Self {
165 func: T::setting_changed,
166 }
167 }
168 }

giterated-plugins/giterated-plugin/src/callback/value.rs

View file
@@ -0,0 +1,117 @@
1 use std::future::Future;
2
3 use giterated_models::{
4 error::OperationError, object::GiteratedObject, value::GiteratedObjectValue,
5 };
6
7 use crate::{new_stack::PluginState, AnyObject, NewAnyValue};
8
9 use super::CallbackPtr;
10
11 #[derive(Copy, Clone)]
12 pub struct ValueGetterCallback {
13 pub callback_ptr: CallbackPtr,
14 pub func: unsafe extern "C" fn(
15 CallbackPtr,
16 &PluginState,
17 object: AnyObject,
18 ) -> Result<NewAnyValue, ()>,
19 }
20
21 impl ValueGetterCallback {
22 pub fn new<S, O, V, T: IntoPluginValueGetter<S, O, V>>(handler: T) -> Self {
23 Self {
24 func: T::get_value,
25 callback_ptr: handler.callback_ptr(),
26 }
27 }
28 }
29
30 pub trait IntoPluginValueGetter<S, O, V> {
31 unsafe extern "C" fn get_value(
32 callback: CallbackPtr,
33 state: &PluginState,
34 object: AnyObject,
35 ) -> Result<NewAnyValue, ()>;
36
37 fn callback_ptr(&self) -> CallbackPtr;
38 }
39
40 impl<F, S, O, V, Fut> IntoPluginValueGetter<S, O, V> for F
41 where
42 Fut: Future<Output = Result<V, OperationError<anyhow::Error>>>,
43 S: Clone,
44 O: GiteratedObject,
45 V: GiteratedObjectValue<Object = O>,
46 F: Fn(S, O) -> Fut,
47 {
48 unsafe extern "C" fn get_value(
49 callback: CallbackPtr,
50 state: &PluginState,
51 mut object: AnyObject,
52 ) -> Result<NewAnyValue, ()> {
53 let _guard = trace_span!(
54 "get_value handler",
55 object = O::object_name(),
56 value = V::value_name()
57 )
58 .entered();
59 let state = unsafe { state.transmute_ref::<S>() };
60
61 let object = unsafe { object.transmute_owned::<O>() };
62
63 // Cast the callback ptr to ourselves
64 let callback: *const F = std::mem::transmute(callback.0);
65 let callback = callback.as_ref().unwrap();
66
67 let result = callback(state.clone(), *object);
68
69 // match result {
70 // Ok(value) => Ok(NewAnyValue::new(value)),
71 // Err(_) => todo!(),
72 // }
73
74 todo!()
75 }
76
77 fn callback_ptr(&self) -> CallbackPtr {
78 unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) }
79 }
80 }
81
82 pub struct ValueChangeCallback {
83 func: unsafe extern "C" fn(
84 &PluginState,
85 object: AnyObject,
86 value_name: &str,
87 new_value: NewAnyValue,
88 ),
89 }
90
91 pub trait IntoValueChangeCallback<S, O> {
92 unsafe extern "C" fn value_changed(
93 state: &PluginState,
94 object: AnyObject,
95 value_name: &str,
96 new_value: NewAnyValue,
97 );
98 }
99
100 impl<F, S, O> IntoValueChangeCallback<S, O> for F {
101 unsafe extern "C" fn value_changed(
102 state: &PluginState,
103 object: AnyObject,
104 value_name: &str,
105 new_value: NewAnyValue,
106 ) {
107 todo!()
108 }
109 }
110
111 impl ValueChangeCallback {
112 pub fn new<S, O, T: IntoValueChangeCallback<S, O>>() -> Self {
113 Self {
114 func: T::value_changed,
115 }
116 }
117 }

giterated-plugins/giterated-plugin/src/handle.rs

View file
@@ -0,0 +1,263 @@
1 use std::{collections::HashMap, marker::PhantomData, path::Path, sync::Arc};
2
3 use anyhow::Error;
4 use dlopen2::wrapper::Container;
5 use semver::Version;
6 use tracing::{debug, trace};
7
8 use crate::{
9 callback::{OperationHandlerCallback, SettingGetterCallback, ValueGetterCallback},
10 new_stack::{
11 ObjectOperationPair, ObjectSettingPair, ObjectValuePair, PluginMeta, PluginState,
12 RuntimeHandlers, TypeMetadata,
13 },
14 FFISettingMeta, FFIValueChangeHandler, FFIValueMeta, GiteratedPluginApi, InitializationVTable,
15 ObjectVtable, OperationVTable, SettingVtable, ValueVTable,
16 };
17
18 #[derive(Clone)]
19 pub struct PluginHandle {
20 pub meta: PluginMeta,
21 pub raw: Arc<Container<GiteratedPluginApi>>,
22 pub initialization: Arc<PluginInitializationState>,
23 pub state: PluginState,
24 }
25
26 impl PluginHandle {
27 pub fn from_dylib(path: &str) -> Result<Self, CreationError> {
28 let mut handle = unsafe { Container::load(path) }?;
29
30 // Initialize the raw handle
31 let init_state = Self::initialize_raw_handle(&mut handle)?;
32
33 let metadata = Self::get_meta(&mut handle)?;
34
35 let initalization = Self::initialize_registration(&mut handle)?;
36
37 trace!(
38 "Loaded plugin {} (Version: {})",
39 metadata.name,
40 metadata.version
41 );
42
43 Ok(Self {
44 raw: Arc::new(handle),
45 meta: metadata,
46 initialization: Arc::new(initalization),
47 state: init_state,
48 })
49 }
50
51 /// Builds the Plugin's Substack.
52 ///
53 /// Builds the Plugin into a substack, which can then be provided to the Giterated Runtime.
54 pub fn build_substack(&mut self) -> Result<(), Error> {
55 todo!()
56 }
57
58 fn get_meta(handle: &mut Container<GiteratedPluginApi>) -> Result<PluginMeta, CreationError> {
59 let meta = unsafe { handle.plugin_meta() };
60
61 let name = unsafe { std::slice::from_raw_parts(meta.name, meta.name_len) };
62 let version = unsafe { std::slice::from_raw_parts(meta.version, meta.version_len) };
63
64 let name = std::str::from_utf8(name).unwrap();
65 let version = std::str::from_utf8(version).unwrap();
66
67 Ok(PluginMeta {
68 name: String::from(name),
69 version: Version::parse(version).unwrap(),
70 })
71 }
72
73 pub fn initialize_registration(
74 handle: &mut Container<GiteratedPluginApi>,
75 ) -> Result<PluginInitializationState, CreationError> {
76 debug!("Initializing plugin registration...");
77 let mut builder = PluginInitializationTable::default();
78
79 // SAFETY: The lifetime of the returned type is only valid as long
80 // as the builder that returned it lives
81 let func_table = unsafe { builder.func_table() };
82
83 let state = Box::new(PluginInitializationState::new());
84
85 unsafe { handle.load_initialization_vtable(&func_table) };
86 let state = unsafe { handle.initialize_registration(Box::into_raw(state)) };
87
88 debug!("Plugin handle initialized!");
89 Ok(unsafe { *Box::from_raw(state) })
90 }
91
92 fn initialize_raw_handle(
93 handle: &mut Container<GiteratedPluginApi>,
94 ) -> Result<PluginState, CreationError> {
95 debug!("Initializing plugin handle...");
96
97 let state = unsafe { handle.initialize() };
98
99 debug!("Plugin handle initialized!");
100
101 Ok(state)
102 }
103 }
104
105 #[derive(Debug, thiserror::Error)]
106 pub enum CreationError {
107 #[error("an error occured opening the library {0}")]
108 LoadingLibrary(#[from] dlopen2::Error),
109 }
110
111 pub struct PluginSubstackBuilder {}
112
113 #[derive(Default)]
114 pub struct PluginInitializationState {
115 pub type_metadata: TypeMetadata,
116 pub operation_handlers: HashMap<ObjectOperationPair<'static>, OperationHandlerCallback>,
117 pub value_getters: HashMap<ObjectValuePair<'static>, ValueGetterCallback>,
118 pub setting_getters: HashMap<ObjectSettingPair<'static>, SettingGetterCallback>,
119 }
120
121 impl PluginInitializationState {
122 pub fn new() -> Self {
123 Self::default()
124 }
125 }
126
127 #[derive(Default)]
128 pub struct PluginInitializationTable<'a> {
129 _marker: PhantomData<&'a ()>,
130 }
131
132 impl<'a> PluginInitializationTable<'a> {
133 pub unsafe fn func_table(&mut self) -> InitializationVTable {
134 InitializationVTable {
135 register_value_change,
136 register_object,
137 register_operation,
138 register_setting,
139 register_value,
140 operation_handler,
141 value_getter,
142 setting_getter,
143 }
144 }
145 }
146
147 unsafe extern "C" fn register_value_change(
148 state: *mut PluginInitializationState,
149 object_kind: &str,
150 value_name: &str,
151 vtable: FFIValueChangeHandler,
152 ) {
153 todo!()
154 }
155
156 unsafe extern "C" fn register_object(
157 state: *mut PluginInitializationState,
158 object_kind: &'static str,
159 vtable: ObjectVtable,
160 ) {
161 let mut state = Box::from_raw(state);
162
163 state.type_metadata.register_object(object_kind, vtable);
164
165 Box::into_raw(state);
166 }
167
168 unsafe extern "C" fn register_operation(
169 state: *mut PluginInitializationState,
170 object_kind: &'static str,
171 operation_name: &'static str,
172 vtable: OperationVTable,
173 ) {
174 let mut state = Box::from_raw(state);
175
176 state
177 .type_metadata
178 .register_operation(object_kind, operation_name, vtable);
179
180 Box::into_raw(state);
181 }
182
183 unsafe extern "C" fn register_setting(
184 state: *mut PluginInitializationState,
185 object_kind: &'static str,
186 setting_name: &'static str,
187 vtable: SettingVtable,
188 ) {
189 let mut state = Box::from_raw(state);
190
191 state
192 .type_metadata
193 .register_setting(object_kind, setting_name, vtable);
194
195 Box::into_raw(state);
196 }
197
198 unsafe extern "C" fn register_value(
199 state: *mut PluginInitializationState,
200 object_kind: &'static str,
201 value_name: &'static str,
202 vtable: ValueVTable,
203 ) {
204 let mut state = Box::from_raw(state);
205
206 state
207 .type_metadata
208 .register_value(object_kind, value_name, vtable);
209
210 Box::into_raw(state);
211 }
212
213 unsafe extern "C" fn operation_handler(
214 state: *mut PluginInitializationState,
215 object_kind: &'static str,
216 operation_name: &'static str,
217 handler: OperationHandlerCallback,
218 ) {
219 let mut state = Box::from_raw(state);
220
221 trace!("Operation handler for {}::{}", object_kind, operation_name);
222
223 state.operation_handlers.insert(
224 ObjectOperationPair::new(object_kind, operation_name),
225 handler,
226 );
227
228 Box::into_raw(state);
229 }
230
231 unsafe extern "C" fn value_getter(
232 state: *mut PluginInitializationState,
233 object_kind: &'static str,
234 value_name: &'static str,
235 handler: ValueGetterCallback,
236 ) {
237 let mut state = Box::from_raw(state);
238
239 trace!("Value getter for {}::{}", object_kind, value_name);
240
241 state
242 .value_getters
243 .insert(ObjectValuePair::new(object_kind, value_name), handler);
244
245 Box::into_raw(state);
246 }
247
248 unsafe extern "C" fn setting_getter(
249 state: *mut PluginInitializationState,
250 object_kind: &'static str,
251 setting_name: &'static str,
252 handler: SettingGetterCallback,
253 ) {
254 let mut state = Box::from_raw(state);
255
256 trace!("Setting getter for {}::{}", object_kind, setting_name);
257
258 state
259 .setting_getters
260 .insert(ObjectSettingPair::new(object_kind, setting_name), handler);
261
262 Box::into_raw(state);
263 }

giterated-plugins/giterated-plugin/src/lib.rs

View file
@@ -0,0 +1,424 @@
1 pub mod callback;
2 pub mod handle;
3 pub mod new_stack;
4 pub mod vtable;
5
6 #[macro_use]
7 extern crate tracing;
8
9 use std::{any::Any, fmt::Debug, mem::transmute, ptr::null_mut, sync::Arc};
10
11 use callback::{OperationHandlerCallback, SettingGetterCallback, ValueGetterCallback};
12 use dlopen2::wrapper::WrapperApi;
13 use giterated_models::{
14 object::GiteratedObject, operation::GiteratedOperation, settings::Setting,
15 value::GiteratedObjectValue,
16 };
17 use handle::PluginInitializationState;
18 use new_stack::{FFIPluginMeta, PluginState};
19
20 #[derive(WrapperApi)]
21 pub struct GiteratedPluginApi {
22 plugin_meta: unsafe extern "C" fn() -> FFIPluginMeta,
23 load_host_vtable: unsafe extern "C" fn(vtable: &HostVTable),
24 load_initialization_vtable: unsafe extern "C" fn(vtable: &InitializationVTable),
25 initialize: unsafe extern "C" fn() -> PluginState,
26 initialize_registration: unsafe extern "C" fn(
27 init_state: *mut PluginInitializationState,
28 ) -> *mut PluginInitializationState,
29 }
30
31 #[repr(C)]
32 pub struct FFIBox<T: ?Sized>(*mut T);
33
34 impl<T: ?Sized> FFIBox<T> {
35 pub fn from_box(src: Box<T>) -> Self {
36 Self(Box::into_raw(src))
37 }
38
39 pub fn untyped(self) -> FFIBox<()> {
40 FFIBox(self.0 as *mut ())
41 }
42 }
43
44 #[repr(C)]
45 pub struct FFIObject(&'static str);
46
47 #[repr(C)]
48 struct FFIOperationHandler(
49 for<'a> unsafe extern "C" fn(operation: FFIOperation<'a>) -> Result<FFIBox<[u8]>, FFIBox<[u8]>>,
50 );
51
52 #[repr(C)]
53 struct FFIOperation<'a> {
54 operation_name: &'a str,
55 object_name: &'a str,
56 object: FFIObject,
57 operation: &'a [u8],
58 }
59
60 #[derive(Clone, Copy)]
61 #[repr(C)]
62 pub struct OperationVTable {
63 pub serialize: unsafe extern "C" fn(AnyObject) -> Result<FFIBox<[u8]>, ()>,
64 pub deserialize: unsafe extern "C" fn(&[u8]) -> Result<AnyOperation, ()>,
65 pub is_same: unsafe extern "C" fn(AnyObject) -> bool,
66 pub serialize_success: unsafe extern "C" fn(()) -> Result<FFIBox<[u8]>, ()>,
67 pub serialize_failure: unsafe extern "C" fn(()) -> Result<FFIBox<[u8]>, ()>,
68 pub deserialize_success: unsafe extern "C" fn(&[u8]) -> Result<(), ()>,
69 pub deserialize_failure: unsafe extern "C" fn(&[u8]) -> Result<(), ()>,
70 }
71
72 impl OperationVTable {
73 pub fn new<O, T: IntoOperationVTable<O>>() -> Self {
74 Self {
75 serialize: T::serialize,
76 deserialize: T::deserialize,
77 is_same: T::is_same,
78 serialize_success: T::serialize_success,
79 serialize_failure: T::serialize_failure,
80 deserialize_success: T::deserialize_success,
81 deserialize_failure: T::deserialize_failure,
82 }
83 }
84 }
85
86 pub struct AnyOperation {
87 /// A pointer to the plugin-local object type. We are not capable of
88 /// knowing what this type is, we use the provided vtable.
89 inner: FFIBox<()>,
90 vtable: OperationVTable,
91 }
92
93 impl AnyOperation {
94 pub unsafe fn transmute_owned<T>(&mut self) -> Box<T> {
95 Box::from_raw(self.inner.0 as *mut T)
96 }
97
98 pub unsafe fn transmute_ref<T>(&self) -> &T {
99 let ptr: *const T = transmute(self.inner.0);
100
101 ptr.as_ref().unwrap()
102 }
103 }
104
105 #[repr(C)]
106 pub struct FFIValueChangeHandler();
107
108 #[derive(Clone, Copy)]
109 #[repr(C)]
110 pub struct ObjectVtable {
111 pub to_str: unsafe extern "C" fn(AnyObject) -> FFIBox<str>,
112 pub from_str: unsafe extern "C" fn(&str) -> Result<AnyObject, FFIBox<str>>,
113 pub home_uri: unsafe extern "C" fn(AnyObject) -> FFIBox<str>,
114 pub is_same: unsafe extern "C" fn(AnyObject) -> bool,
115 }
116
117 impl ObjectVtable {
118 pub const fn new<T: IntoObjectVTable>() -> Self {
119 Self {
120 to_str: T::to_str,
121 from_str: T::from_str,
122 home_uri: T::home_uri,
123 is_same: T::is_same,
124 }
125 }
126 }
127
128 #[repr(C)]
129 pub struct NewAnySetting {
130 /// A pointer to the plugin-local object type. We are not capable of
131 /// knowing what this type is, we use the provided vtable.
132 inner: FFIBox<()>,
133 vtable: SettingVtable,
134 }
135
136 impl NewAnySetting {
137 pub fn new<V: IntoSettingVTable>(value: V) -> Self {
138 Self {
139 inner: FFIBox::from_box(Box::new(value)).untyped(),
140 vtable: SettingVtable::new::<V>(),
141 }
142 }
143 }
144
145 #[derive(Clone, Copy)]
146 #[repr(C)]
147 pub struct SettingVtable {
148 pub deserialize: unsafe extern "C" fn(&[u8]) -> Result<(), ()>,
149 pub serialize: unsafe extern "C" fn(NewAnySetting) -> Result<FFIBox<[u8]>, ()>,
150 }
151
152 impl SettingVtable {
153 pub fn new<T: IntoSettingVTable>() -> Self {
154 Self {
155 deserialize: T::deserialize,
156 serialize: T::serialize,
157 }
158 }
159 }
160
161 #[repr(C)]
162 pub struct NewAnyValue {
163 /// A pointer to the plugin-local object type. We are not capable of
164 /// knowing what this type is, we use the provided vtable.
165 inner: FFIBox<()>,
166 vtable: ValueVTable,
167 }
168
169 impl NewAnyValue {
170 pub fn new<O, V: IntoValueVTable<O>>(value: V) -> Self {
171 NewAnyValue {
172 inner: FFIBox::from_box(Box::new(value)).untyped(),
173 vtable: ValueVTable::new::<O, V>(),
174 }
175 }
176 }
177
178 #[derive(Clone, Copy)]
179 #[repr(C)]
180 pub struct ValueVTable {
181 pub deserialize: unsafe extern "C" fn(&[u8]) -> Result<NewAnyValue, ()>,
182 pub serialize: unsafe extern "C" fn(NewAnyValue) -> Result<FFIBox<[u8]>, ()>,
183 }
184
185 impl ValueVTable {
186 pub fn new<O, T: IntoValueVTable<O>>() -> Self {
187 Self {
188 deserialize: T::deserialize,
189 serialize: T::serialize,
190 }
191 }
192 }
193
194 pub trait IntoObjectVTable {
195 unsafe extern "C" fn to_str(this: AnyObject) -> FFIBox<str>;
196 unsafe extern "C" fn from_str(src: &str) -> Result<AnyObject, FFIBox<str>>;
197 unsafe extern "C" fn home_uri(this: AnyObject) -> FFIBox<str>;
198 unsafe extern "C" fn is_same(other: AnyObject) -> bool;
199 }
200
201 impl<T: GiteratedObject + 'static> IntoObjectVTable for T {
202 unsafe extern "C" fn to_str(mut this: AnyObject) -> FFIBox<str> {
203 let this: &Box<T> = this.transmute_ref();
204
205 let result = this.to_string();
206
207 FFIBox::from_box(result.into_boxed_str())
208 }
209
210 unsafe extern "C" fn from_str(src: &str) -> Result<AnyObject, FFIBox<str>> {
211 let result = T::from_object_str(src).unwrap();
212
213 let any_object = AnyObject::new(result);
214
215 Ok(any_object)
216 }
217
218 unsafe extern "C" fn home_uri(this: AnyObject) -> FFIBox<str> {
219 todo!()
220 }
221
222 unsafe extern "C" fn is_same(other: AnyObject) -> bool {
223 todo!()
224 }
225 }
226
227 pub trait IntoOperationVTable<O> {
228 unsafe extern "C" fn serialize(this: AnyObject) -> Result<FFIBox<[u8]>, ()>;
229 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<AnyOperation, ()>;
230 unsafe extern "C" fn is_same(this: AnyObject) -> bool;
231 unsafe extern "C" fn serialize_success(success: ()) -> Result<FFIBox<[u8]>, ()>;
232 unsafe extern "C" fn serialize_failure(failure: ()) -> Result<FFIBox<[u8]>, ()>;
233 unsafe extern "C" fn deserialize_success(src: &[u8]) -> Result<(), ()>;
234 unsafe extern "C" fn deserialize_failure(src: &[u8]) -> Result<(), ()>;
235 }
236
237 impl<O, D> IntoOperationVTable<O> for D
238 where
239 D: GiteratedOperation<O>,
240 O: GiteratedObject,
241 {
242 unsafe extern "C" fn serialize(this: AnyObject) -> Result<FFIBox<[u8]>, ()> {
243 todo!()
244 }
245
246 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<AnyOperation, ()> {
247 let deserialized: D = serde_json::from_slice(src).unwrap();
248
249 Ok(AnyOperation {
250 inner: FFIBox::from_box(Box::new(deserialized)).untyped(),
251 vtable: OperationVTable::new::<O, D>(),
252 })
253 }
254
255 unsafe extern "C" fn is_same(this: AnyObject) -> bool {
256 todo!()
257 }
258
259 unsafe extern "C" fn serialize_success(success: ()) -> Result<FFIBox<[u8]>, ()> {
260 todo!()
261 }
262
263 unsafe extern "C" fn serialize_failure(failure: ()) -> Result<FFIBox<[u8]>, ()> {
264 todo!()
265 }
266
267 unsafe extern "C" fn deserialize_success(src: &[u8]) -> Result<(), ()> {
268 todo!()
269 }
270
271 unsafe extern "C" fn deserialize_failure(src: &[u8]) -> Result<(), ()> {
272 todo!()
273 }
274 }
275
276 pub trait IntoSettingVTable {
277 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<(), ()>;
278 unsafe extern "C" fn serialize(this: NewAnySetting) -> Result<FFIBox<[u8]>, ()>;
279 }
280
281 impl<S> IntoSettingVTable for S
282 where
283 S: Setting,
284 {
285 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<(), ()> {
286 todo!()
287 }
288
289 unsafe extern "C" fn serialize(this: NewAnySetting) -> Result<FFIBox<[u8]>, ()> {
290 todo!()
291 }
292 }
293
294 pub trait IntoValueVTable<O> {
295 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<NewAnyValue, ()>;
296 unsafe extern "C" fn serialize(this: NewAnyValue) -> Result<FFIBox<[u8]>, ()>;
297 }
298
299 impl<O, V> IntoValueVTable<O> for V
300 where
301 O: GiteratedObject,
302 V: GiteratedObjectValue<Object = O>,
303 {
304 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<NewAnyValue, ()> {
305 let _guard = trace_span!(
306 "deserialize value",
307 object = O::object_name(),
308 value = V::value_name()
309 );
310
311 trace!("Deserializing");
312 let deserialized: V = serde_json::from_slice(src).unwrap();
313
314 Ok(NewAnyValue {
315 inner: FFIBox::from_box(Box::new(deserialized)).untyped(),
316 vtable: ValueVTable::new::<O, V>(),
317 })
318 }
319
320 unsafe extern "C" fn serialize(this: NewAnyValue) -> Result<FFIBox<[u8]>, ()> {
321 todo!()
322 }
323 }
324
325 #[repr(C)]
326 pub struct AnyObject {
327 /// A pointer to the plugin-local object type. We are not capable of
328 /// knowing what this type is, we use the provided vtable.
329 inner: FFIBox<()>,
330 vtable: ObjectVtable,
331 }
332
333 impl AnyObject {
334 pub fn new<T: IntoObjectVTable>(inner: T) -> Self {
335 Self {
336 inner: FFIBox::from_box(Box::new(inner)).untyped(),
337 vtable: ObjectVtable::new::<T>(),
338 }
339 }
340 }
341
342 impl AnyObject {
343 pub unsafe fn transmute_owned<T>(&mut self) -> Box<T> {
344 Box::from_raw(self.inner.0 as *mut T)
345 }
346
347 pub unsafe fn transmute_ref<T>(&self) -> &T {
348 let ptr: *const T = transmute(self.inner.0);
349
350 ptr.as_ref().unwrap()
351 }
352 }
353
354 #[repr(C)]
355 pub struct FFISettingMeta();
356
357 #[repr(C)]
358 pub struct FFIValueMeta();
359
360 #[repr(C)]
361 pub struct HostVTable {
362 register_operation: unsafe extern "C" fn(&str, &str, FFIOperationHandler),
363 register_value_change: unsafe extern "C" fn(&str, &str, FFIValueChangeHandler),
364 register_object: unsafe extern "C" fn(&str, ObjectVtable),
365 register_setting: unsafe extern "C" fn(&str, &str, FFISettingMeta),
366 register_value: unsafe extern "C" fn(&str, &str, FFIValueMeta),
367 }
368
369 #[repr(C)]
370 #[derive(Clone, Copy)]
371 pub struct InitializationVTable {
372 pub register_value_change:
373 unsafe extern "C" fn(*mut PluginInitializationState, &str, &str, FFIValueChangeHandler),
374 pub register_object:
375 unsafe extern "C" fn(*mut PluginInitializationState, &'static str, ObjectVtable),
376 pub register_operation: unsafe extern "C" fn(
377 *mut PluginInitializationState,
378 &'static str,
379 &'static str,
380 OperationVTable,
381 ),
382 pub register_setting: unsafe extern "C" fn(
383 *mut PluginInitializationState,
384 &'static str,
385 &'static str,
386 SettingVtable,
387 ),
388 pub register_value: unsafe extern "C" fn(
389 *mut PluginInitializationState,
390 &'static str,
391 &'static str,
392 ValueVTable,
393 ),
394
395 pub operation_handler: unsafe extern "C" fn(
396 *mut PluginInitializationState,
397 &'static str,
398 &'static str,
399 OperationHandlerCallback,
400 ),
401
402 pub value_getter: unsafe extern "C" fn(
403 *mut PluginInitializationState,
404 &'static str,
405 &'static str,
406 ValueGetterCallback,
407 ),
408
409 pub setting_getter: unsafe extern "C" fn(
410 *mut PluginInitializationState,
411 &'static str,
412 &'static str,
413 SettingGetterCallback,
414 ),
415 }
416
417 impl Debug for InitializationVTable {
418 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
419 f.debug_struct("InitializationVTable").finish()
420 }
421 }
422
423 unsafe impl Sync for InitializationVTable {}
424 unsafe impl Send for InitializationVTable {}

giterated-plugins/giterated-plugin/src/new_stack/mod.rs

View file
@@ -0,0 +1,452 @@
1 pub mod operation_walker;
2
3 use std::{
4 any::type_name, collections::HashMap, fmt::Debug, mem::transmute, ptr::null_mut, sync::Arc,
5 };
6
7 use semver::Version;
8 use tracing::{debug, debug_span, field::DebugValue, span, trace, trace_span, warn, Level};
9
10 use crate::{
11 callback::{
12 OperationHandlerCallback, SettingChangeCallback, SettingGetterCallback,
13 ValueChangeCallback, ValueGetterCallback,
14 },
15 handle::{PluginHandle, PluginInitializationState},
16 AnyObject, AnyOperation, FFIBox, NewAnySetting, NewAnyValue, ObjectVtable, OperationVTable,
17 SettingVtable, ValueVTable,
18 };
19
20 use self::operation_walker::OperationHandlerRules;
21
22 #[derive(Default)]
23 pub struct TypeMetadata {
24 pub objects: HashMap<&'static str, ObjectVtable>,
25 pub operations: HashMap<ObjectOperationPair<'static>, OperationVTable>,
26 pub settings: HashMap<ObjectSettingPair<'static>, SettingVtable>,
27 pub values: HashMap<ObjectValuePair<'static>, ValueVTable>,
28 }
29
30 impl TypeMetadata {
31 pub fn register_object(&mut self, object_kind: &'static str, vtable: ObjectVtable) {
32 trace!("Registering type metadata for {}", object_kind);
33
34 self.objects.insert(object_kind, vtable);
35 }
36
37 pub fn register_operation(
38 &mut self,
39 object_kind: &'static str,
40 operation_name: &'static str,
41 vtable: OperationVTable,
42 ) {
43 trace!(
44 "Registering operation metadata for {}::{}",
45 object_kind,
46 operation_name
47 );
48
49 self.operations.insert(
50 ObjectOperationPair {
51 object_kind,
52 operation_name,
53 },
54 vtable,
55 );
56 }
57
58 pub fn register_setting(
59 &mut self,
60 object_kind: &'static str,
61 setting_name: &'static str,
62 vtable: SettingVtable,
63 ) {
64 trace!("Registering setting {}::{}", object_kind, setting_name);
65
66 self.settings.insert(
67 ObjectSettingPair {
68 object_kind,
69 setting_name,
70 },
71 vtable,
72 );
73 }
74
75 pub fn register_value(
76 &mut self,
77 object_kind: &'static str,
78 value_name: &'static str,
79 vtable: ValueVTable,
80 ) {
81 trace!("Registering value {}::{}", object_kind, value_name);
82
83 self.values.insert(
84 ObjectValuePair {
85 object_kind,
86 value_name,
87 },
88 vtable,
89 );
90 }
91 }
92
93 #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
94 pub struct ObjectOperationPair<'s> {
95 pub object_kind: &'s str,
96 pub operation_name: &'s str,
97 }
98
99 impl<'s> ObjectOperationPair<'s> {
100 pub fn new(object_kind: &'s str, operation_name: &'s str) -> Self {
101 Self {
102 object_kind,
103 operation_name,
104 }
105 }
106 }
107
108 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
109 pub struct ObjectSettingPair<'s> {
110 pub object_kind: &'s str,
111 pub setting_name: &'s str,
112 }
113
114 impl<'s> ObjectSettingPair<'s> {
115 pub fn new(object_kind: &'s str, setting_name: &'s str) -> Self {
116 Self {
117 object_kind,
118 setting_name,
119 }
120 }
121 }
122
123 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
124 pub struct ObjectValuePair<'s> {
125 pub object_kind: &'s str,
126 pub value_name: &'s str,
127 }
128
129 impl<'s> ObjectValuePair<'s> {
130 pub fn new(object_kind: &'s str, value_name: &'s str) -> Self {
131 Self {
132 object_kind,
133 value_name,
134 }
135 }
136 }
137
138 #[derive(Clone, Copy)]
139 #[repr(C)]
140 pub struct PluginState {
141 pub inner: *mut (),
142 }
143
144 impl PluginState {
145 pub unsafe fn transmute_owned<T>(&mut self) -> Box<T> {
146 Box::from_raw(self.inner as *mut T)
147 }
148
149 pub unsafe fn transmute_ref<T>(&self) -> &T {
150 let ptr: *const T = transmute(self.inner);
151
152 ptr.as_ref().unwrap()
153 }
154 }
155
156 impl PluginState {
157 pub fn null() -> Self {
158 Self { inner: null_mut() }
159 }
160 }
161
162 #[derive(Default)]
163 pub struct Runtime {
164 plugins: Vec<(PluginMeta, PluginHandle)>,
165 handlers: RuntimeHandlers,
166 }
167
168 impl Runtime {
169 pub fn insert_plugin(&mut self, mut plugin: PluginHandle) {
170 let _guard = debug_span!("inserting plugin", meta = debug(&plugin.meta)).entered();
171
172 for (pair, callback) in &plugin.initialization.operation_handlers {
173 let _guard =
174 trace_span!("processing operation handler callbacks", pair = debug(pair)).entered();
175
176 if self
177 .handlers
178 .operation_handlers
179 .insert(*pair, (RuntimeDomain::from_plugin(&plugin), *callback))
180 .is_some()
181 {
182 warn!("Warning! Insertion of handler for overwrote a previous handler.")
183 }
184
185 trace!("Insertion of operation handler successful")
186 }
187
188 for (pair, callback) in &plugin.initialization.value_getters {
189 let _guard =
190 trace_span!("processing value getter callbacks", pair = debug(pair)).entered();
191
192 if self
193 .handlers
194 .value_getters
195 .insert(*pair, (RuntimeDomain::from_plugin(&plugin), *callback))
196 .is_some()
197 {
198 warn!("Warning! Insertion of handler for overwrote a previous handler.")
199 }
200
201 trace!("Insertion of operation handler successful")
202 }
203
204 for (pair, callback) in &plugin.initialization.setting_getters {
205 let _guard =
206 trace_span!("processing setting getter callbacks", pair = debug(pair)).entered();
207
208 if self
209 .handlers
210 .setting_getters
211 .insert(*pair, (RuntimeDomain::from_plugin(&plugin), *callback))
212 .is_some()
213 {
214 warn!("Warning! Insertion of setting handler for overwrote a previous handler.")
215 }
216
217 trace!("Insertion of setting handler successful")
218 }
219 }
220
221 pub fn handle(
222 &self,
223 object_kind: &str,
224 operation_name: &str,
225 object: &str,
226 operation_payload: &[u8],
227 ) -> Result<(), HandlerError> {
228 let rules = self.handlers.handle_operation(object_kind, operation_name);
229
230 rules.handle(object, operation_payload)
231 }
232 }
233
234 #[derive(Default)]
235 pub struct RuntimeHandlers {
236 operation_handlers:
237 HashMap<ObjectOperationPair<'static>, (RuntimeDomain, OperationHandlerCallback)>,
238 value_getters: HashMap<ObjectValuePair<'static>, (RuntimeDomain, ValueGetterCallback)>,
239 setting_getters: HashMap<ObjectSettingPair<'static>, (RuntimeDomain, SettingGetterCallback)>,
240 value_change: HashMap<ObjectValuePair<'static>, (RuntimeDomain, ValueChangeCallback)>,
241 setting_change: HashMap<ObjectSettingPair<'static>, (RuntimeDomain, SettingChangeCallback)>,
242 }
243
244 impl RuntimeHandlers {
245 pub fn operation_handler(
246 &mut self,
247 pair: ObjectOperationPair<'static>,
248 handler: OperationHandlerCallback,
249 domain: RuntimeDomain,
250 ) {
251 trace!(
252 "Inserting operation handler for {}::{}",
253 pair.object_kind,
254 pair.operation_name
255 );
256
257 // There can only be one handler per operation (at least for now?), send a warning if
258 // a newly registered handler overwrites the previous handler.
259 if self
260 .operation_handlers
261 .insert(pair, (domain, handler))
262 .is_some()
263 {
264 debug!("Warning! A newly inserted operation handler for {}::{} overwrites a previous handler.", pair.object_kind, pair.operation_name);
265 }
266 }
267
268 pub fn value_getter(
269 &mut self,
270 pair: ObjectValuePair<'static>,
271 handler: ValueGetterCallback,
272 domain: RuntimeDomain,
273 ) {
274 trace!(
275 "Inserting value getter for {}::{}",
276 pair.object_kind,
277 pair.value_name
278 );
279
280 if self.value_getters.insert(pair, (domain, handler)).is_some() {
281 debug!(
282 "Warning! A newly inserted value getter for {}::{} overwrites a previous handler.",
283 pair.object_kind, pair.value_name
284 );
285 }
286 }
287
288 pub fn setting_getter(
289 &mut self,
290 pair: ObjectSettingPair<'static>,
291 handler: SettingGetterCallback,
292 domain: RuntimeDomain,
293 ) {
294 trace!(
295 "Inserting setting getter for {}::{}",
296 pair.object_kind,
297 pair.setting_name
298 );
299
300 if self
301 .setting_getters
302 .insert(pair, (domain, handler))
303 .is_some()
304 {
305 debug!("Warning! A newly inserted setting getter for {}::{} overwrites a previous handler.", pair.object_kind, pair.setting_name);
306 }
307 }
308
309 pub fn value_change(
310 &mut self,
311 pair: ObjectValuePair<'static>,
312 handler: ValueChangeCallback,
313 domain: RuntimeDomain,
314 ) {
315 trace!(
316 "Inserting value change handler for {}::{}",
317 pair.object_kind,
318 pair.value_name
319 );
320
321 if self.value_change.insert(pair, (domain, handler)).is_some() {
322 debug!("Warning! A newly inserted value change handler for {}::{} overwrites a previous handler.", pair.object_kind, pair.value_name);
323 panic!("Not intended");
324 }
325 }
326
327 pub fn setting_change(
328 &mut self,
329 pair: ObjectSettingPair<'static>,
330 handler: SettingChangeCallback,
331 domain: RuntimeDomain,
332 ) {
333 trace!(
334 "Inserting setting change handler for {}::{}",
335 pair.object_kind,
336 pair.setting_name
337 );
338
339 if self
340 .setting_change
341 .insert(pair, (domain, handler))
342 .is_some()
343 {
344 debug!("Warning! A newly inserted setting change handler for {}::{} overwrites a previous handler.", pair.object_kind, pair.setting_name);
345 panic!("Not intended");
346 }
347 }
348 }
349
350 impl RuntimeHandlers {
351 pub fn handle_operation<'o>(
352 &'o self,
353 object_kind: &'o str,
354 operation_name: &'o str,
355 ) -> OperationHandlerRules<'o> {
356 OperationHandlerRules::new(object_kind, operation_name, self)
357 }
358 }
359
360 pub struct RuntimeDomain {
361 plugin: PluginHandle,
362 }
363
364 impl RuntimeDomain {
365 pub fn from_plugin(plugin: &PluginHandle) -> Self {
366 Self {
367 plugin: plugin.clone(),
368 }
369 }
370
371 pub fn object_vtable(&self, object_kind: &str) -> Option<ObjectVtable> {
372 self.plugin
373 .initialization
374 .type_metadata
375 .objects
376 .get(object_kind)
377 .copied()
378 }
379
380 pub fn operation_vtable(
381 &self,
382 object_kind: &str,
383 operation_name: &str,
384 ) -> Option<OperationVTable> {
385 self.plugin
386 .initialization
387 .type_metadata
388 .operations
389 .get(&ObjectOperationPair::new(object_kind, operation_name))
390 .copied()
391 }
392
393 pub fn setting_vtable(&self, object_kind: &str, setting_name: &str) -> Option<SettingVtable> {
394 self.plugin
395 .initialization
396 .type_metadata
397 .settings
398 .get(&ObjectSettingPair::new(object_kind, setting_name))
399 .copied()
400 }
401
402 pub fn value_vtable(&self, object_kind: &str, value_name: &str) -> Option<ValueVTable> {
403 self.plugin
404 .initialization
405 .type_metadata
406 .values
407 .get(&ObjectValuePair::new(object_kind, value_name))
408 .copied()
409 }
410 }
411
412 #[derive(Clone, Debug)]
413 pub struct PluginMeta {
414 pub name: String,
415 pub version: Version,
416 }
417
418 #[repr(C)]
419 pub struct FFIPluginMeta {
420 pub name: *const u8,
421 pub name_len: usize,
422 pub version: *const u8,
423 pub version_len: usize,
424 }
425
426 pub struct RuntimePlugin {
427 handle: PluginHandle,
428 type_metadata: Arc<TypeMetadata>,
429 }
430
431 impl RuntimePlugin {
432 pub fn plugin_meta(&self) -> PluginMeta {
433 let meta = unsafe { self.handle.raw.plugin_meta() };
434
435 let name = unsafe { std::slice::from_raw_parts(meta.name, meta.name_len) };
436 let version = unsafe { std::slice::from_raw_parts(meta.version, meta.version_len) };
437
438 let name = std::str::from_utf8(name).unwrap();
439 let version = std::str::from_utf8(version).unwrap();
440
441 PluginMeta {
442 name: String::from(name),
443 version: Version::parse(version).unwrap(),
444 }
445 }
446 }
447
448 pub enum HandlerError {
449 Failure(()),
450 Internal(()),
451 Unhandled,
452 }

giterated-plugins/giterated-plugin/src/new_stack/operation_walker.rs

View file
@@ -0,0 +1,225 @@
1 use giterated_models::{operation::GiteratedOperation, settings::GetSetting, value::GetValue};
2 use tracing::{debug_span, trace, trace_span};
3
4 use crate::new_stack::{ObjectOperationPair, PluginState};
5
6 use super::{HandlerError, ObjectSettingPair, ObjectValuePair, RuntimeHandlers};
7
8 /// A wrapper for operation handling that enforces handling rules.
9 ///
10 /// # Handler Resolution
11 /// In order, handler resolution will be attempted as follows:
12 ///
13 /// | Index | object_kind | operation_kind | Special Case? |
14 /// |-------|-------------|-----------------|---------------|
15 /// | 1 | `any` | `typed` | No |
16 /// | 2 | `typed` | `any` | No |
17 /// | 3 | `any` | `any` | No |
18 /// | 4 | `any` | `GetValue` | ⚠️ Yes ⚠️ |
19 /// | 5 | `any` | `GetSetting` | ⚠️ Yes ⚠️ |
20 /// | 6 | `any` | `SetSetting` | ⚠️ Yes ⚠️ |
21 /// | 7 | `any` | `ObjectRequest` | ⚠️ Yes ⚠️ |
22 /// | 8 | `typed` | `typed` | No |
23 pub struct OperationHandlerRules<'a> {
24 object_kind: &'a str,
25 operation_name: &'a str,
26 handlers: &'a RuntimeHandlers,
27 }
28
29 impl<'o> OperationHandlerRules<'o> {
30 pub fn new(
31 object_kind: &'o str,
32 operation_name: &'o str,
33 handlers: &'o RuntimeHandlers,
34 ) -> Self {
35 Self {
36 object_kind,
37 operation_name,
38 handlers,
39 }
40 }
41
42 pub fn handle(&self, object: &str, operation_payload: &[u8]) -> Result<(), HandlerError> {
43 // object_kind: `any`
44 // operation_kind: `typed`
45 if let Some(handler) = self
46 .handlers
47 .operation_handlers
48 .get(&ObjectOperationPair::new("any", self.operation_name))
49 {
50 todo!()
51 }
52
53 // object_kind: `typed`
54 // operation_kind: `any`
55 if let Some(handler) = self
56 .handlers
57 .operation_handlers
58 .get(&ObjectOperationPair::new(self.object_kind, "any"))
59 {}
60
61 // object_kind: `any`
62 // operation_kind: `any`
63 if let Some(handler) = self
64 .handlers
65 .operation_handlers
66 .get(&ObjectOperationPair::new("any", "any"))
67 {}
68
69 // ⚠️ Special Case ⚠️
70 // object_kind: `any`
71 // operation_kind: `GetValue`
72 if self.operation_name == "get_value" {
73 let operation: GetValue = serde_json::from_slice(operation_payload).unwrap();
74 let _guard = trace_span!(
75 "get_value handler resolving",
76 object = self.object_kind,
77 value = operation.value_name
78 )
79 .entered();
80
81 if let Some((domain, callback)) = self.handlers.value_getters.get(
82 &ObjectValuePair::new(self.object_kind, &operation.value_name),
83 ) {
84 trace_span!(
85 "get_value handler.",
86 object = self.object_kind,
87 value_name = operation.value_name
88 );
89
90 let object_vtable = domain
91 .object_vtable(self.object_kind)
92 .ok_or_else(|| HandlerError::Unhandled)?;
93 trace!("Resolved object vtable for {}", self.object_kind);
94
95 let value_vtable = domain
96 .value_vtable(self.object_kind, &operation.value_name)
97 .ok_or_else(|| HandlerError::Unhandled)?;
98 trace!(
99 "Resolved value vtable for {}::{}",
100 self.object_kind,
101 operation.value_name
102 );
103
104 let object = unsafe { (object_vtable.from_str)(object) }
105 .map_err(|_| HandlerError::Internal(()))?;
106
107 let _guard = debug_span!("get_value handler");
108
109 let result =
110 unsafe { (callback.func)(callback.callback_ptr, &domain.plugin.state, object) };
111
112 // Todo deser
113
114 return Ok(());
115 } else {
116 trace!("Failed to resolve handler.");
117 }
118 }
119
120 // ⚠️ Special Case ⚠️
121 // object_kind: `any`
122 // operation_kind: `GetSetting`
123 if self.operation_name == "get_setting" {
124 let operation: GetSetting = serde_json::from_slice(operation_payload).unwrap();
125 let _guard = trace_span!(
126 "get_setting handler resolving",
127 object = self.object_kind,
128 setting = operation.setting_name
129 )
130 .entered();
131
132 if let Some((domain, callback)) = self.handlers.setting_getters.get(
133 &ObjectSettingPair::new(self.object_kind, &operation.setting_name),
134 ) {
135 trace_span!(
136 "get_setting handler.",
137 object = self.object_kind,
138 setting_name = operation.setting_name
139 );
140
141 let object_vtable = domain
142 .object_vtable(self.object_kind)
143 .ok_or_else(|| HandlerError::Unhandled)?;
144 trace!("Resolved object vtable for {}", self.object_kind);
145
146 let setting_vtable = domain
147 .setting_vtable(self.object_kind, &operation.setting_name)
148 .ok_or_else(|| HandlerError::Unhandled)?;
149 trace!("Resolved setting vtable for {}", operation.setting_name);
150
151 let object = unsafe { (object_vtable.from_str)(object) }
152 .map_err(|_| HandlerError::Internal(()))?;
153
154 let _guard = debug_span!("get_value handler");
155
156 let result =
157 unsafe { (callback.func)(callback.callback_ptr, &domain.plugin.state, object) };
158
159 // Todo deser
160
161 return Ok(());
162 } else {
163 trace!("Failed to resolve handler.");
164 }
165 }
166
167 // ⚠️ Special Case ⚠️
168 // object_kind: `any`
169 // operation_kind: `SetSetting`
170 if self.operation_name == "set_setting" {}
171
172 // ⚠️ Special Case ⚠️
173 // object_kind: `any`
174 // operation_kind: `ObjectRequest`
175 if self.operation_name == "object_request" {}
176
177 // object_kind: `typed`
178 // operation_kind: `typed`
179 if let Some((domain, handler)) =
180 self.handlers
181 .operation_handlers
182 .get(&ObjectOperationPair::new(
183 self.object_kind,
184 self.operation_name,
185 ))
186 {
187 let _guard = trace_span!("typed_typed handler resolved").entered();
188
189 let object_vtable = domain
190 .object_vtable(self.object_kind)
191 .ok_or_else(|| HandlerError::Unhandled)?;
192 trace!("Resolved object vtable for {}", self.object_kind);
193
194 let operation_vtable = domain
195 .operation_vtable(self.object_kind, self.operation_name)
196 .ok_or_else(|| HandlerError::Unhandled)?;
197 trace!(
198 "Resolved operation vtable for {}::{}",
199 self.object_kind,
200 self.operation_name
201 );
202
203 let object = unsafe { (object_vtable.from_str)(object) }
204 .map_err(|_| HandlerError::Internal(()))?;
205 let operation = unsafe { (operation_vtable.deserialize)(operation_payload) }
206 .map_err(|_| HandlerError::Internal(()))?;
207 trace!("Parsed operation data");
208
209 let _guard = debug_span!("calling handler").entered();
210 let result = unsafe {
211 (handler.func)(
212 handler.callback_ptr,
213 &domain.plugin.state,
214 object,
215 operation,
216 )
217 };
218
219 // todo
220 return Ok(());
221 }
222
223 Err(HandlerError::Unhandled)
224 }
225 }

giterated-plugins/giterated-plugin/src/vtable/mod.rs

View file
@@ -0,0 +1,3 @@
1 //! Giterated's VTable System
2 //!
3 //! Docs here? :)