So. Much. Work.
parent: tbd commit: b05f964
Showing 31 changed files with 2747 insertions and 8 deletions
Cargo.lock
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 | } |