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

ambee/giterated

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

Fucking whatever there you go

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨57c2ca5

Showing ⁨⁨106⁩ changed files⁩ with ⁨⁨3840⁩ insertions⁩ and ⁨⁨9693⁩ deletions⁩

Cargo.lock

View file
@@ -105,15 +105,6 @@ dependencies = [
105 105 ]
106 106
107 107 [[package]]
108 name = "async-lock"
109 version = "2.8.0"
110 source = "registry+https://github.com/rust-lang/crates.io-index"
111 checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
112 dependencies = [
113 "event-listener",
114 ]
115
116 [[package]]
117 108 name = "async-trait"
118 109 version = "0.1.74"
119 110 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -215,12 +206,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
215 206 checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
216 207
217 208 [[package]]
218 name = "bytecount"
219 version = "0.6.7"
220 source = "registry+https://github.com/rust-lang/crates.io-index"
221 checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
222
223 [[package]]
224 209 name = "byteorder"
225 210 version = "1.5.0"
226 211 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -233,37 +218,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
233 218 checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
234 219
235 220 [[package]]
236 name = "camino"
237 version = "1.1.6"
238 source = "registry+https://github.com/rust-lang/crates.io-index"
239 checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
240 dependencies = [
241 "serde",
242 ]
243
244 [[package]]
245 name = "cargo-platform"
246 version = "0.1.4"
247 source = "registry+https://github.com/rust-lang/crates.io-index"
248 checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36"
249 dependencies = [
250 "serde",
251 ]
252
253 [[package]]
254 name = "cargo_metadata"
255 version = "0.14.2"
256 source = "registry+https://github.com/rust-lang/crates.io-index"
257 checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
258 dependencies = [
259 "camino",
260 "cargo-platform",
261 "semver",
262 "serde",
263 "serde_json",
264 ]
265
266 [[package]]
267 221 name = "cc"
268 222 version = "1.0.83"
269 223 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -351,29 +305,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
351 305 checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
352 306
353 307 [[package]]
354 name = "crossbeam-channel"
355 version = "0.5.8"
356 source = "registry+https://github.com/rust-lang/crates.io-index"
357 checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
358 dependencies = [
359 "cfg-if",
360 "crossbeam-utils",
361 ]
362
363 [[package]]
364 name = "crossbeam-epoch"
365 version = "0.9.15"
366 source = "registry+https://github.com/rust-lang/crates.io-index"
367 checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
368 dependencies = [
369 "autocfg",
370 "cfg-if",
371 "crossbeam-utils",
372 "memoffset",
373 "scopeguard",
374 ]
375
376 [[package]]
377 308 name = "crossbeam-queue"
378 309 version = "0.3.8"
379 310 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -533,15 +464,6 @@ dependencies = [
533 464 ]
534 465
535 466 [[package]]
536 name = "error-chain"
537 version = "0.12.4"
538 source = "registry+https://github.com/rust-lang/crates.io-index"
539 checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
540 dependencies = [
541 "version_check",
542 ]
543
544 [[package]]
545 467 name = "etcetera"
546 468 version = "0.8.0"
547 469 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -780,24 +702,6 @@ dependencies = [
780 702 ]
781 703
782 704 [[package]]
783 name = "giterated-cache"
784 version = "0.1.0"
785 dependencies = [
786 "anyhow",
787 "async-trait",
788 "bincode",
789 "futures-util",
790 "giterated-models",
791 "giterated-stack",
792 "moka",
793 "serde",
794 "serde_json",
795 "thiserror",
796 "tokio",
797 "tracing",
798 ]
799
800 [[package]]
801 705 name = "giterated-daemon"
802 706 version = "0.1.0"
803 707 dependencies = [
@@ -922,28 +826,6 @@ dependencies = [
922 826 ]
923 827
924 828 [[package]]
925 name = "giterated-stack"
926 version = "0.1.0"
927 dependencies = [
928 "anyhow",
929 "async-trait",
930 "bincode",
931 "futures-util",
932 "giterated-models",
933 "serde",
934 "serde_json",
935 "thiserror",
936 "tokio",
937 "tracing",
938 ]
939
940 [[package]]
941 name = "glob"
942 version = "0.3.1"
943 source = "registry+https://github.com/rust-lang/crates.io-index"
944 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
945
946 [[package]]
947 829 name = "h2"
948 830 version = "0.3.21"
949 831 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1316,15 +1198,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1316 1198 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
1317 1199
1318 1200 [[package]]
1319 name = "mach2"
1320 version = "0.4.1"
1321 source = "registry+https://github.com/rust-lang/crates.io-index"
1322 checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
1323 dependencies = [
1324 "libc",
1325 ]
1326
1327 [[package]]
1328 1201 name = "md-5"
1329 1202 version = "0.10.6"
1330 1203 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1341,15 +1214,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1341 1214 checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
1342 1215
1343 1216 [[package]]
1344 name = "memoffset"
1345 version = "0.9.0"
1346 source = "registry+https://github.com/rust-lang/crates.io-index"
1347 checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
1348 dependencies = [
1349 "autocfg",
1350 ]
1351
1352 [[package]]
1353 1217 name = "mime"
1354 1218 version = "0.3.17"
1355 1219 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1382,30 +1246,6 @@ dependencies = [
1382 1246 ]
1383 1247
1384 1248 [[package]]
1385 name = "moka"
1386 version = "0.12.1"
1387 source = "registry+https://github.com/rust-lang/crates.io-index"
1388 checksum = "d8017ec3548ffe7d4cef7ac0e12b044c01164a74c0f3119420faeaf13490ad8b"
1389 dependencies = [
1390 "async-lock",
1391 "async-trait",
1392 "crossbeam-channel",
1393 "crossbeam-epoch",
1394 "crossbeam-utils",
1395 "futures-util",
1396 "once_cell",
1397 "parking_lot",
1398 "quanta",
1399 "rustc_version",
1400 "skeptic",
1401 "smallvec",
1402 "tagptr",
1403 "thiserror",
1404 "triomphe",
1405 "uuid",
1406 ]
1407
1408 [[package]]
1409 1249 name = "native-tls"
1410 1250 version = "0.2.11"
1411 1251 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1721,33 +1561,6 @@ dependencies = [
1721 1561 ]
1722 1562
1723 1563 [[package]]
1724 name = "pulldown-cmark"
1725 version = "0.9.3"
1726 source = "registry+https://github.com/rust-lang/crates.io-index"
1727 checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
1728 dependencies = [
1729 "bitflags 1.3.2",
1730 "memchr",
1731 "unicase",
1732 ]
1733
1734 [[package]]
1735 name = "quanta"
1736 version = "0.11.1"
1737 source = "registry+https://github.com/rust-lang/crates.io-index"
1738 checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab"
1739 dependencies = [
1740 "crossbeam-utils",
1741 "libc",
1742 "mach2",
1743 "once_cell",
1744 "raw-cpuid",
1745 "wasi",
1746 "web-sys",
1747 "winapi",
1748 ]
1749
1750 [[package]]
1751 1564 name = "quote"
1752 1565 version = "1.0.33"
1753 1566 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1787,15 +1600,6 @@ dependencies = [
1787 1600 ]
1788 1601
1789 1602 [[package]]
1790 name = "raw-cpuid"
1791 version = "10.7.0"
1792 source = "registry+https://github.com/rust-lang/crates.io-index"
1793 checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
1794 dependencies = [
1795 "bitflags 1.3.2",
1796 ]
1797
1798 [[package]]
1799 1603 name = "redox_syscall"
1800 1604 version = "0.4.1"
1801 1605 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1890,15 +1694,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1890 1694 checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
1891 1695
1892 1696 [[package]]
1893 name = "rustc_version"
1894 version = "0.4.0"
1895 source = "registry+https://github.com/rust-lang/crates.io-index"
1896 checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
1897 dependencies = [
1898 "semver",
1899 ]
1900
1901 [[package]]
1902 1697 name = "rustix"
1903 1698 version = "0.38.21"
1904 1699 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1918,15 +1713,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1918 1713 checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
1919 1714
1920 1715 [[package]]
1921 name = "same-file"
1922 version = "1.0.6"
1923 source = "registry+https://github.com/rust-lang/crates.io-index"
1924 checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
1925 dependencies = [
1926 "winapi-util",
1927 ]
1928
1929 [[package]]
1930 1716 name = "schannel"
1931 1717 version = "0.1.22"
1932 1718 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2098,21 +1884,6 @@ dependencies = [
2098 1884 ]
2099 1885
2100 1886 [[package]]
2101 name = "skeptic"
2102 version = "0.13.7"
2103 source = "registry+https://github.com/rust-lang/crates.io-index"
2104 checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8"
2105 dependencies = [
2106 "bytecount",
2107 "cargo_metadata",
2108 "error-chain",
2109 "glob",
2110 "pulldown-cmark",
2111 "tempfile",
2112 "walkdir",
2113 ]
2114
2115 [[package]]
2116 1887 name = "slab"
2117 1888 version = "0.4.9"
2118 1889 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2444,12 +2215,6 @@ dependencies = [
2444 2215 ]
2445 2216
2446 2217 [[package]]
2447 name = "tagptr"
2448 version = "0.2.0"
2449 source = "registry+https://github.com/rust-lang/crates.io-index"
2450 checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
2451
2452 [[package]]
2453 2218 name = "tempfile"
2454 2219 version = "3.8.1"
2455 2220 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2714,12 +2479,6 @@ dependencies = [
2714 2479 ]
2715 2480
2716 2481 [[package]]
2717 name = "triomphe"
2718 version = "0.1.9"
2719 source = "registry+https://github.com/rust-lang/crates.io-index"
2720 checksum = "0eee8098afad3fb0c54a9007aab6804558410503ad676d4633f9c2559a00ac0f"
2721
2722 [[package]]
2723 2482 name = "try-lock"
2724 2483 version = "0.2.4"
2725 2484 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2751,15 +2510,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2751 2510 checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
2752 2511
2753 2512 [[package]]
2754 name = "unicase"
2755 version = "2.7.0"
2756 source = "registry+https://github.com/rust-lang/crates.io-index"
2757 checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
2758 dependencies = [
2759 "version_check",
2760 ]
2761
2762 [[package]]
2763 2513 name = "unicode-bidi"
2764 2514 version = "0.3.13"
2765 2515 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2827,15 +2577,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2827 2577 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
2828 2578
2829 2579 [[package]]
2830 name = "uuid"
2831 version = "1.5.0"
2832 source = "registry+https://github.com/rust-lang/crates.io-index"
2833 checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
2834 dependencies = [
2835 "getrandom",
2836 ]
2837
2838 [[package]]
2839 2580 name = "valuable"
2840 2581 version = "0.1.0"
2841 2582 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2854,16 +2595,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2854 2595 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
2855 2596
2856 2597 [[package]]
2857 name = "walkdir"
2858 version = "2.4.0"
2859 source = "registry+https://github.com/rust-lang/crates.io-index"
2860 checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
2861 dependencies = [
2862 "same-file",
2863 "winapi-util",
2864 ]
2865
2866 [[package]]
2867 2598 name = "want"
2868 2599 version = "0.3.1"
2869 2600 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2977,15 +2708,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2977 2708 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
2978 2709
2979 2710 [[package]]
2980 name = "winapi-util"
2981 version = "0.1.6"
2982 source = "registry+https://github.com/rust-lang/crates.io-index"
2983 checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
2984 dependencies = [
2985 "winapi",
2986 ]
2987
2988 [[package]]
2989 2711 name = "winapi-x86_64-pc-windows-gnu"
2990 2712 version = "0.4.0"
2991 2713 source = "registry+https://github.com/rust-lang/crates.io-index"

Cargo.toml

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

giterated-daemon/Cargo.toml

View file
@@ -30,8 +30,8 @@ argon2 = "0.5"
30 30 aes-gcm = "0.10"
31 31 semver = {version = "1.0", features = ["serde"]}
32 32 giterated-models = { path = "../giterated-models" }
33 giterated-plugin = { path = "../giterated-plugins/giterated-plugin" }
34 giterated-protocol = { path = "../giterated-plugins/giterated-protocol" }
33 giterated-plugin = { path = "../giterated-plugin" }
34 giterated-protocol = { path = "../plugins/giterated-protocol" }
35 35 deadpool = "0.9"
36 36 bincode = "1.3"
37 37 tokio-util = {version = "0.7", features = ["rt"]}

giterated-plugin/Cargo.toml

View file
@@ -0,0 +1,16 @@
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"
16 async-trait = "0.1"

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

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

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

View file
@@ -0,0 +1,270 @@
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 CallbackPtr, IntoPluginOperationHandler, IntoPluginSettingGetter, IntoPluginSettingSetter,
10 IntoPluginValueGetter, OperationHandlerCallback, SettingGetterCallback,
11 ValueGetterCallback,
12 },
13 handle::PluginInitializationState,
14 new_stack::PluginState,
15 vtable::{
16 InitializationVTable, IntoObjectVTable, IntoOperationVTable, IntoSettingVTable,
17 IntoValueVTable, ObjectVtable, OperationVTable, SettingVtable, ValueVTable,
18 },
19 AnyObject, NewAnyValue,
20 };
21 use tracing::trace_span;
22
23 pub struct PluginStackBuilder<'init, S> {
24 init_state: *mut PluginInitializationState,
25 vtable: &'init InitializationVTable,
26 state: S,
27 }
28
29 impl<'init, S> PluginStackBuilder<'init, S> {
30 pub fn new(
31 plugin_state: S,
32 state: *mut PluginInitializationState,
33 vtable: &'init InitializationVTable,
34 ) -> Self {
35 Self {
36 init_state: state,
37 vtable,
38 state: plugin_state,
39 }
40 }
41
42 pub fn object<O: IntoObjectVTable + GiteratedObject>(&mut self) -> &mut Self {
43 let _guard = trace_span!("register object").entered();
44
45 let func = self.vtable.register_object;
46
47 unsafe { func(self.init_state, O::object_name(), ObjectVtable::new::<O>()) };
48
49 self
50 }
51
52 pub fn register_operation<O, D>(&mut self) -> &mut Self
53 where
54 D: IntoOperationVTable<O> + GiteratedOperation<O>,
55 O: GiteratedObject,
56 {
57 let _guard = trace_span!("register operation").entered();
58
59 unsafe {
60 (self.vtable.register_operation)(
61 self.init_state,
62 O::object_name(),
63 D::operation_name(),
64 OperationVTable::new::<O, D>(),
65 )
66 }
67
68 self
69 }
70
71 pub fn object_setting<O, OS, HG, HS>(&mut self, get: HG, set: HS) -> &mut Self
72 where
73 O: GiteratedObject,
74 OS: IntoSettingVTable + Setting,
75 HG: IntoPluginSettingGetter<S, O, OS>,
76 HS: IntoPluginSettingSetter<S, O, OS>,
77 {
78 let _guard = trace_span!("register setting").entered();
79
80 unsafe {
81 (self.vtable.register_setting)(
82 self.init_state,
83 O::object_name(),
84 OS::name(),
85 SettingVtable::new::<OS>(),
86 )
87 }
88
89 self
90 }
91
92 pub fn object_user_setting<O, OS>(&mut self) -> &mut Self
93 where
94 O: GiteratedObject,
95 OS: IntoSettingVTable + Setting,
96 {
97 let _guard = trace_span!("register setting").entered();
98
99 unsafe {
100 (self.vtable.register_setting)(
101 self.init_state,
102 O::object_name(),
103 OS::name(),
104 SettingVtable::new::<OS>(),
105 )
106 }
107
108 self
109 }
110
111 pub fn value<O, V, T>(&mut self, handler: T) -> &mut Self
112 where
113 O: GiteratedObject,
114 V: IntoValueVTable<O> + GiteratedObjectValue<Object = O>,
115 T: IntoPluginValueGetter<S, O, V>,
116 {
117 let _guard = trace_span!("register value").entered();
118
119 unsafe {
120 (self.vtable.register_value)(
121 self.init_state,
122 O::object_name(),
123 V::value_name(),
124 ValueVTable::new::<O, V>(),
125 )
126 }
127
128 unsafe {
129 (self.vtable.value_getter)(
130 self.init_state,
131 O::object_name(),
132 V::value_name(),
133 ValueGetterCallback::new::<S, O, V, T>(handler),
134 )
135 }
136
137 self
138 }
139
140 pub fn operation<
141 DS,
142 DF,
143 A,
144 O: GiteratedObject + IntoObjectVTable,
145 D: IntoOperationVTable<O> + GiteratedOperation<O>,
146 T: IntoPluginOperationHandler<S, O, D, DS, DF, A>,
147 >(
148 &mut self,
149 handler: T,
150 ) -> &mut Self {
151 let _guard = trace_span!("register operation handler").entered();
152
153 unsafe {
154 (self.vtable.operation_handler)(
155 self.init_state,
156 O::object_name(),
157 D::operation_name(),
158 OperationHandlerCallback::new::<S, O, D, DS, DF, A, T>(handler),
159 )
160 }
161
162 // TODO: Yikes?
163 self.object::<O>();
164
165 self.register_operation::<O, D>();
166
167 self
168 }
169
170 // pub fn value_getter<O, V, T>(&mut self, handler: T) -> &mut Self
171 // where
172 // O: GiteratedObject + IntoObjectVTable,
173 // V: GiteratedObjectValue<Object = O> + IntoValueVTable<O>,
174 // T: IntoPluginValueGetter<S, O, V>,
175 // {
176 // let _guard = trace_span!("register value_getter handler").entered();
177
178 // unsafe {
179 // (self.vtable.value_getter)(
180 // self.init_state,
181 // O::object_name(),
182 // V::value_name(),
183 // ValueGetterCallback::new::<S, O, V, T>(handler),
184 // )
185 // }
186
187 // // TODO: Yikes?
188 // self.object::<O>();
189 // self.value::<O, V>();
190
191 // self
192 // }
193
194 // pub fn setting_getter<O, OS, T>(&mut self, handler: T) -> &mut Self
195 // where
196 // O: GiteratedObject + IntoObjectVTable,
197 // OS: Setting + IntoSettingVTable,
198 // T: IntoPluginSettingGetter<S, O, OS>,
199 // {
200 // let _guard = trace_span!("register setting_getter handler").entered();
201
202 // unsafe {
203 // (self.vtable.setting_getter)(
204 // self.init_state,
205 // O::object_name(),
206 // OS::name(),
207 // SettingGetterCallback::new::<S, O, OS, T>(handler),
208 // )
209 // }
210
211 // self.object::<O>();
212 // // self.setting::<O, OS>();
213
214 // self
215 // }
216 }
217
218 pub trait ValueSettingExt<PS> {
219 fn value_setting<O, VS, HG, HS>(&mut self, get: HG, set: HS) -> &mut Self
220 where
221 O: GiteratedObject + IntoObjectVTable + 'static,
222 VS: GiteratedObjectValue<Object = O> + IntoValueVTable<O> + Setting + IntoSettingVTable,
223 HG: IntoPluginSettingGetter<PS, O, VS>,
224 HS: IntoPluginSettingSetter<PS, O, VS>;
225 }
226
227 impl<PS> ValueSettingExt<PS> for PluginStackBuilder<'_, PS> {
228 fn value_setting<O, VS, HG, HS>(&mut self, get: HG, set: HS) -> &mut Self
229 where
230 O: GiteratedObject + IntoObjectVTable + 'static,
231 VS: GiteratedObjectValue<Object = O> + IntoValueVTable<O> + Setting + IntoSettingVTable,
232 HG: IntoPluginSettingGetter<PS, O, VS>,
233 HS: IntoPluginSettingSetter<PS, O, VS>,
234 {
235 self
236 }
237 }
238
239 pub trait ValueSettingGetter<S, O, V> {
240 unsafe extern "C" fn get_value(
241 callback: CallbackPtr,
242 state: &PluginState,
243 object: AnyObject,
244 ) -> Result<NewAnyValue, ()>;
245
246 fn callback_ptr(&self) -> CallbackPtr;
247 }
248
249 impl<S, O, VS, HG> ValueSettingGetter<S, O, VS> for HG
250 where
251 O: GiteratedObject,
252 VS: GiteratedObjectValue<Object = O>,
253 HG: IntoPluginSettingGetter<S, O, VS>,
254 {
255 unsafe extern "C" fn get_value(
256 callback: CallbackPtr,
257 state: &PluginState,
258 object: AnyObject,
259 ) -> Result<NewAnyValue, ()> {
260 let result = HG::get_setting(callback, state, object)?;
261
262 let setting = *result.transmute_owned::<VS>();
263
264 Ok(NewAnyValue::new(setting))
265 }
266
267 fn callback_ptr(&self) -> CallbackPtr {
268 self.callback_ptr()
269 }
270 }

giterated-plugin/src/callback/mod.rs

View file
@@ -0,0 +1,28 @@
1 mod operation;
2 use std::sync::Arc;
3
4 pub use operation::*;
5 mod value;
6 pub use value::*;
7 mod setting;
8 pub use setting::*;
9
10 use crate::new_stack::{runtime_handler::RuntimeHandle, PluginState, Runtime};
11
12 /// A container for a callback pointer, used to provide an internal callback function or
13 /// state to a plugin when performing a callback.
14 #[derive(Clone, Copy)]
15 #[repr(C)]
16 pub struct CallbackPtr(*const ());
17
18 impl CallbackPtr {
19 pub unsafe fn from_raw(callback: *const ()) -> Self {
20 Self(callback)
21 }
22 }
23
24 #[repr(C)]
25 pub struct RuntimeState {
26 pub runtime: RuntimeHandle,
27 pub operation_state: PluginState,
28 }

giterated-plugin/src/callback/operation.rs

View file
@@ -0,0 +1,176 @@
1 use giterated_models::error::OperationError;
2
3 use crate::{
4 new_stack::{runtime_handler::RuntimeHandle, PluginState, Runtime, State},
5 AnyObject, AnyOperation,
6 };
7
8 use std::{any::type_name, fmt::Debug, future::Future, sync::Arc};
9
10 use super::{CallbackPtr, RuntimeState};
11
12 #[derive(Clone, Copy)]
13 pub struct OperationHandlerCallback {
14 pub callback_ptr: CallbackPtr,
15 pub func: unsafe extern "C" fn(
16 CallbackPtr,
17 &RuntimeState,
18 &PluginState,
19 object: AnyObject,
20 operation: AnyOperation,
21 ),
22 }
23
24 impl OperationHandlerCallback {
25 pub fn new<S, O, D, DS, DF, A, T: IntoPluginOperationHandler<S, O, D, DS, DF, A>>(
26 handler: T,
27 ) -> Self {
28 OperationHandlerCallback {
29 func: T::handle,
30 callback_ptr: T::callback_ptr(&handler),
31 }
32 }
33 }
34
35 pub trait IntoPluginOperationHandler<S, O, D, DS, DF, A> {
36 unsafe extern "C" fn handle(
37 callback_ptr: CallbackPtr,
38 runtime_state: &RuntimeState,
39 state: &PluginState,
40 object: AnyObject,
41 operation: AnyOperation,
42 );
43 fn callback_ptr(&self) -> CallbackPtr;
44 }
45
46 impl<F, S, O, D, DS, DF, Fut> IntoPluginOperationHandler<S, O, D, DS, DF, ()> for F
47 where
48 Fut: Future<Output = Result<DS, OperationError<DF>>>,
49 F: Fn(S, O, D) -> Fut,
50 S: Clone + Debug,
51 O: Debug,
52 D: Debug,
53 {
54 unsafe extern "C" fn handle(
55 callback: CallbackPtr,
56 runtime_state: &RuntimeState,
57 state: &PluginState,
58 mut object: AnyObject,
59 mut operation: AnyOperation,
60 ) {
61 let _guard = trace_span!(
62 "operation handler",
63 object = type_name::<O>(),
64 operation = type_name::<D>()
65 )
66 .entered();
67 let state = unsafe { state.transmute_ref::<S>() };
68
69 // Since this is Rust code, we know that the AnyObject and AnyOperation are just boxes
70 let object = unsafe { object.transmute_owned::<O>() };
71 let operation = unsafe { operation.transmute_owned::<D>() };
72
73 // Cast the callback ptr to ourselves
74 let callback: *const F = std::mem::transmute(callback.0);
75 let callback = callback.as_ref().unwrap();
76
77 // callback(state.clone(), *object, *operation)
78
79 todo!()
80 }
81
82 fn callback_ptr(&self) -> CallbackPtr {
83 unsafe { CallbackPtr::from_raw(self as *const _ as *const ()) }
84 }
85 }
86
87 impl<F, S, O, D, DS, DF, Fut, A1> IntoPluginOperationHandler<S, O, D, DS, DF, (A1,)> for F
88 where
89 Fut: Future<Output = Result<DS, OperationError<DF>>>,
90 F: Fn(S, O, D, A1) -> Fut,
91 S: Clone + Debug,
92 O: Debug,
93 D: Debug,
94 {
95 unsafe extern "C" fn handle(
96 callback_ptr: CallbackPtr,
97 runtime_state: &RuntimeState,
98 state: &PluginState,
99 object: AnyObject,
100 operation: AnyOperation,
101 ) {
102 todo!()
103 }
104
105 fn callback_ptr(&self) -> CallbackPtr {
106 todo!()
107 }
108 }
109
110 impl<F, S, O, D, DS, DF, Fut, A1, A2> IntoPluginOperationHandler<S, O, D, DS, DF, (A1, A2)> for F
111 where
112 Fut: Future<Output = Result<DS, OperationError<DF>>>,
113 F: Fn(S, O, D, A1, A2) -> Fut,
114 S: Clone + Debug,
115 O: Debug,
116 D: Debug,
117 {
118 unsafe extern "C" fn handle(
119 callback_ptr: CallbackPtr,
120 runtime_state: &RuntimeState,
121 state: &PluginState,
122 object: AnyObject,
123 operation: AnyOperation,
124 ) {
125 todo!()
126 }
127
128 fn callback_ptr(&self) -> CallbackPtr {
129 todo!()
130 }
131 }
132
133 pub trait FromOperationState<S, O, D>: Sized {
134 fn from_operation_state(
135 state: &S,
136 runtime_state: &RuntimeState,
137 object: &O,
138 operation: &D,
139 ) -> Result<Self, OperationError<anyhow::Error>>;
140 }
141
142 impl<S, O, D> FromOperationState<S, O, D> for RuntimeHandle {
143 fn from_operation_state(
144 state: &S,
145 runtime_state: &RuntimeState,
146 object: &O,
147 operation: &D,
148 ) -> Result<Self, OperationError<anyhow::Error>> {
149 Ok(runtime_state.runtime.clone())
150 }
151 }
152
153 impl<S, O, D, T> FromOperationState<S, O, D> for Option<T>
154 where
155 T: FromOperationState<S, O, D>,
156 {
157 fn from_operation_state(
158 state: &S,
159 runtime_state: &RuntimeState,
160 object: &O,
161 operation: &D,
162 ) -> Result<Self, OperationError<anyhow::Error>> {
163 Ok(T::from_operation_state(state, runtime_state, object, operation).ok())
164 }
165 }
166
167 impl<S, O, D, OS: Clone> FromOperationState<S, O, D> for State<OS> {
168 fn from_operation_state(
169 state: &S,
170 runtime_state: &RuntimeState,
171 object: &O,
172 operation: &D,
173 ) -> Result<Self, OperationError<anyhow::Error>> {
174 Ok(unsafe { State(runtime_state.operation_state.transmute_ref::<OS>().clone()) })
175 }
176 }

giterated-plugin/src/callback/setting.rs

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

giterated-plugin/src/callback/value.rs

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

giterated-plugin/src/handle.rs

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

giterated-plugin/src/lib.rs

View file
@@ -0,0 +1,59 @@
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 pub use vtable::{AnyFailure, AnyObject, AnyOperation, AnySuccess, NewAnySetting, NewAnyValue};
21 use vtable::{HostVTable, InitializationVTable};
22
23 #[derive(WrapperApi)]
24 pub struct GiteratedPluginApi {
25 plugin_meta: unsafe extern "C" fn() -> FFIPluginMeta,
26 load_host_vtable: unsafe extern "C" fn(vtable: &HostVTable),
27 load_initialization_vtable: unsafe extern "C" fn(vtable: &InitializationVTable),
28 initialize: unsafe extern "C" fn() -> PluginState,
29 initialize_registration: unsafe extern "C" fn(
30 init_state: *mut PluginInitializationState,
31 ) -> *mut PluginInitializationState,
32 }
33
34 #[repr(C)]
35 pub struct FFIBox<T: ?Sized>(*mut T);
36
37 impl<T: ?Sized> FFIBox<T> {
38 pub fn from_box(src: Box<T>) -> Self {
39 Self(Box::into_raw(src))
40 }
41
42 pub fn untyped(self) -> FFIBox<()> {
43 FFIBox(self.0 as *mut ())
44 }
45 }
46
47 impl<T: ?Sized> AsRef<T> for FFIBox<T> {
48 fn as_ref(&self) -> &T {
49 todo!()
50 }
51 }
52
53 impl<T: ?Sized> std::ops::Deref for FFIBox<T> {
54 type Target = T;
55
56 fn deref(&self) -> &Self::Target {
57 unsafe { self.0.as_ref() }.unwrap()
58 }
59 }

giterated-plugin/src/new_stack/mod.rs

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

giterated-plugin/src/new_stack/operation_walker.rs

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

giterated-plugin/src/new_stack/runtime_handler.rs

View file
@@ -0,0 +1,76 @@
1 use std::fmt::Debug;
2 use std::sync::Arc;
3
4 use giterated_models::{
5 error::OperationError,
6 object::{GiteratedObject, Object, ObjectRequestError},
7 object_backend::ObjectBackend,
8 operation::GiteratedOperation,
9 };
10
11 use crate::vtable::{AnyFailure, AnySuccess, OperationVTable};
12
13 use super::PluginState;
14
15 #[derive(Clone)]
16 pub struct RuntimeHandle {
17 inner: Arc<RuntimeHandleInner>,
18 }
19
20 impl RuntimeHandle {
21 pub async fn handle_serialized(
22 &self,
23 operation_name: &str,
24 object: &str,
25 operation: &[u8],
26 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
27 todo!()
28 }
29 }
30
31 #[repr(C)]
32 struct RuntimeHandleInner {
33 state: PluginState,
34 handle_serialized: unsafe extern "C" fn(
35 object_kind: &str,
36 operation_name: &str,
37 object: &str,
38 operation_payload: &[u8],
39 ) -> HandlerResult,
40 }
41
42 unsafe impl Send for RuntimeHandleInner {}
43 unsafe impl Sync for RuntimeHandleInner {}
44
45 #[repr(C)]
46 struct HandlerResult {
47 operation_vtable: OperationVTable,
48 result: Result<AnySuccess, OperationError<AnyFailure>>,
49 }
50
51 #[async_trait::async_trait(?Send)]
52 impl<S: Send + Sync + Clone> ObjectBackend<S> for RuntimeHandle {
53 async fn object_operation<O, D>(
54 &self,
55 object: O,
56 operation: &str,
57 payload: D,
58 operation_state: &S,
59 ) -> Result<D::Success, OperationError<D::Failure>>
60 where
61 O: GiteratedObject + Debug + 'static,
62 D: GiteratedOperation<O> + Debug + 'static,
63 D::Success: Clone,
64 D::Failure: Clone,
65 {
66 todo!()
67 }
68
69 async fn get_object<O: GiteratedObject + Debug + 'static>(
70 &self,
71 object_str: &str,
72 operation_state: &S,
73 ) -> Result<Object<S, O, Self>, OperationError<ObjectRequestError>> {
74 todo!()
75 }
76 }

giterated-plugin/src/vtable/host.rs

View file
@@ -0,0 +1,64 @@
1 use super::{ObjectVtable, OperationVTable, SettingVtable, ValueVTable};
2 use crate::{
3 callback::{OperationHandlerCallback, SettingGetterCallback, ValueGetterCallback},
4 handle::PluginInitializationState,
5 };
6 use std::fmt::Debug;
7
8 #[repr(C)]
9 pub struct HostVTable {}
10
11 #[repr(C)]
12 #[derive(Clone, Copy)]
13 pub struct InitializationVTable {
14 pub register_object:
15 unsafe extern "C" fn(*mut PluginInitializationState, &'static str, ObjectVtable),
16 pub register_operation: unsafe extern "C" fn(
17 *mut PluginInitializationState,
18 &'static str,
19 &'static str,
20 OperationVTable,
21 ),
22 pub register_setting: unsafe extern "C" fn(
23 *mut PluginInitializationState,
24 &'static str,
25 &'static str,
26 SettingVtable,
27 ),
28 pub register_value: unsafe extern "C" fn(
29 *mut PluginInitializationState,
30 &'static str,
31 &'static str,
32 ValueVTable,
33 ),
34
35 pub operation_handler: unsafe extern "C" fn(
36 *mut PluginInitializationState,
37 &'static str,
38 &'static str,
39 OperationHandlerCallback,
40 ),
41
42 pub value_getter: unsafe extern "C" fn(
43 *mut PluginInitializationState,
44 &'static str,
45 &'static str,
46 ValueGetterCallback,
47 ),
48
49 pub setting_getter: unsafe extern "C" fn(
50 *mut PluginInitializationState,
51 &'static str,
52 &'static str,
53 SettingGetterCallback,
54 ),
55 }
56
57 impl Debug for InitializationVTable {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 f.debug_struct("InitializationVTable").finish()
60 }
61 }
62
63 unsafe impl Sync for InitializationVTable {}
64 unsafe impl Send for InitializationVTable {}

giterated-plugin/src/vtable/mod.rs

View file
@@ -0,0 +1,13 @@
1 //! Giterated's VTable System
2 //!
3 //! Docs here? :)
4 mod setting;
5 pub use setting::*;
6 mod operation;
7 pub use operation::*;
8 mod object;
9 pub use object::*;
10 mod value;
11 pub use value::*;
12 mod host;
13 pub use host::*;

giterated-plugin/src/vtable/object.rs

View file
@@ -0,0 +1,109 @@
1 use std::mem::transmute;
2
3 use giterated_models::object::GiteratedObject;
4
5 use crate::FFIBox;
6
7 #[derive(Clone, Copy)]
8 #[repr(C)]
9 pub struct ObjectVtable {
10 object_kind: *const u8,
11 object_kind_len: usize,
12 pub to_str: unsafe extern "C" fn(AnyObject) -> FFIBox<str>,
13 pub from_str: unsafe extern "C" fn(&str) -> Result<AnyObject, FFIBox<str>>,
14 pub home_uri: unsafe extern "C" fn(&AnyObject) -> FFIBox<str>,
15 pub is_same: unsafe extern "C" fn(AnyObject) -> bool,
16 }
17
18 impl ObjectVtable {
19 pub fn new<T: IntoObjectVTable>() -> Self {
20 let object_kind = T::object_kind().as_ptr();
21 let object_kind_len = T::object_kind().len();
22
23 Self {
24 to_str: T::to_str,
25 from_str: T::from_str,
26 home_uri: T::home_uri,
27 is_same: T::is_same,
28 object_kind,
29 object_kind_len,
30 }
31 }
32
33 pub fn kind(&self) -> &'static str {
34 let slice = unsafe { std::slice::from_raw_parts(self.object_kind, self.object_kind_len) };
35
36 std::str::from_utf8(slice).unwrap()
37 }
38 }
39
40 pub trait IntoObjectVTable {
41 fn object_kind() -> &'static str;
42 unsafe extern "C" fn to_str(this: AnyObject) -> FFIBox<str>;
43 unsafe extern "C" fn from_str(src: &str) -> Result<AnyObject, FFIBox<str>>;
44 unsafe extern "C" fn home_uri(this: &AnyObject) -> FFIBox<str>;
45 unsafe extern "C" fn is_same(other: AnyObject) -> bool;
46 }
47
48 impl<T: GiteratedObject + 'static> IntoObjectVTable for T {
49 unsafe extern "C" fn to_str(mut this: AnyObject) -> FFIBox<str> {
50 let this: &Box<T> = this.transmute_ref();
51
52 let result = this.to_string();
53
54 FFIBox::from_box(result.into_boxed_str())
55 }
56
57 unsafe extern "C" fn from_str(src: &str) -> Result<AnyObject, FFIBox<str>> {
58 let result = T::from_object_str(src).unwrap();
59
60 let any_object = AnyObject::new(result);
61
62 Ok(any_object)
63 }
64
65 unsafe extern "C" fn home_uri(this: &AnyObject) -> FFIBox<str> {
66 todo!()
67 }
68
69 unsafe extern "C" fn is_same(other: AnyObject) -> bool {
70 todo!()
71 }
72
73 fn object_kind() -> &'static str {
74 todo!()
75 }
76 }
77
78 #[repr(C)]
79 pub struct AnyObject {
80 /// A pointer to the plugin-local object type. We are not capable of
81 /// knowing what this type is, we use the provided vtable.
82 inner: FFIBox<()>,
83 vtable: ObjectVtable,
84 }
85
86 impl AnyObject {
87 pub fn new<T: IntoObjectVTable>(inner: T) -> Self {
88 Self {
89 inner: FFIBox::from_box(Box::new(inner)).untyped(),
90 vtable: ObjectVtable::new::<T>(),
91 }
92 }
93
94 pub fn vtable(&self) -> ObjectVtable {
95 self.vtable
96 }
97 }
98
99 impl AnyObject {
100 pub unsafe fn transmute_owned<T>(&mut self) -> Box<T> {
101 Box::from_raw(self.inner.0 as *mut T)
102 }
103
104 pub unsafe fn transmute_ref<T>(&self) -> &T {
105 let ptr: *const T = transmute(self.inner.0);
106
107 ptr.as_ref().unwrap()
108 }
109 }

giterated-plugin/src/vtable/operation.rs

View file
@@ -0,0 +1,136 @@
1 use std::mem::transmute;
2
3 use giterated_models::{object::GiteratedObject, operation::GiteratedOperation};
4
5 use crate::FFIBox;
6
7 use super::AnyObject;
8
9 #[derive(Clone, Copy)]
10 #[repr(C)]
11 pub struct OperationVTable {
12 operation_kind: *const u8,
13 operation_kind_len: usize,
14 pub serialize: unsafe extern "C" fn(&AnyOperation) -> Result<FFIBox<[u8]>, ()>,
15 pub deserialize: unsafe extern "C" fn(&[u8]) -> Result<AnyOperation, ()>,
16 pub is_same: unsafe extern "C" fn(AnyObject) -> bool,
17 pub serialize_success: unsafe extern "C" fn(()) -> Result<FFIBox<[u8]>, ()>,
18 pub serialize_failure: unsafe extern "C" fn(()) -> Result<FFIBox<[u8]>, ()>,
19 pub deserialize_success: unsafe extern "C" fn(&[u8]) -> Result<AnySuccess, ()>,
20 pub deserialize_failure: unsafe extern "C" fn(&[u8]) -> Result<AnyFailure, ()>,
21 }
22
23 impl OperationVTable {
24 pub fn new<O, T: IntoOperationVTable<O>>() -> Self {
25 let operation_kind = T::operation_kind().as_ptr();
26 let operation_kind_len = T::operation_kind().len();
27
28 Self {
29 serialize: T::serialize,
30 deserialize: T::deserialize,
31 is_same: T::is_same,
32 serialize_success: T::serialize_success,
33 serialize_failure: T::serialize_failure,
34 deserialize_success: T::deserialize_success,
35 deserialize_failure: T::deserialize_failure,
36 operation_kind,
37 operation_kind_len,
38 }
39 }
40
41 pub fn kind(&self) -> &'static str {
42 let slice =
43 unsafe { std::slice::from_raw_parts(self.operation_kind, self.operation_kind_len) };
44
45 std::str::from_utf8(slice).unwrap()
46 }
47 }
48
49 pub struct AnyOperation {
50 /// A pointer to the plugin-local object type. We are not capable of
51 /// knowing what this type is, we use the provided vtable.
52 inner: FFIBox<()>,
53 vtable: OperationVTable,
54 }
55
56 impl AnyOperation {
57 pub unsafe fn transmute_owned<T>(&mut self) -> Box<T> {
58 Box::from_raw(self.inner.0 as *mut T)
59 }
60
61 pub unsafe fn transmute_ref<T>(&self) -> &T {
62 let ptr: *const T = transmute(self.inner.0);
63
64 ptr.as_ref().unwrap()
65 }
66
67 pub fn vtable(&self) -> OperationVTable {
68 self.vtable
69 }
70 }
71
72 #[repr(C)]
73 pub struct AnySuccess {
74 inner: FFIBox<()>,
75 vtable: OperationVTable,
76 }
77
78 #[repr(C)]
79 pub struct AnyFailure {
80 inner: FFIBox<()>,
81 vtable: OperationVTable,
82 }
83
84 pub trait IntoOperationVTable<O> {
85 fn operation_kind() -> &'static str;
86 unsafe extern "C" fn serialize(this: &AnyOperation) -> Result<FFIBox<[u8]>, ()>;
87 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<AnyOperation, ()>;
88 unsafe extern "C" fn is_same(this: AnyObject) -> bool;
89 unsafe extern "C" fn serialize_success(success: ()) -> Result<FFIBox<[u8]>, ()>;
90 unsafe extern "C" fn serialize_failure(failure: ()) -> Result<FFIBox<[u8]>, ()>;
91 unsafe extern "C" fn deserialize_success(src: &[u8]) -> Result<AnySuccess, ()>;
92 unsafe extern "C" fn deserialize_failure(src: &[u8]) -> Result<AnyFailure, ()>;
93 }
94
95 impl<O, D> IntoOperationVTable<O> for D
96 where
97 D: GiteratedOperation<O>,
98 O: GiteratedObject,
99 {
100 unsafe extern "C" fn serialize(this: &AnyOperation) -> Result<FFIBox<[u8]>, ()> {
101 todo!()
102 }
103
104 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<AnyOperation, ()> {
105 let deserialized: D = serde_json::from_slice(src).unwrap();
106
107 Ok(AnyOperation {
108 inner: FFIBox::from_box(Box::new(deserialized)).untyped(),
109 vtable: OperationVTable::new::<O, D>(),
110 })
111 }
112
113 unsafe extern "C" fn is_same(this: AnyObject) -> bool {
114 todo!()
115 }
116
117 unsafe extern "C" fn serialize_success(success: ()) -> Result<FFIBox<[u8]>, ()> {
118 todo!()
119 }
120
121 unsafe extern "C" fn serialize_failure(failure: ()) -> Result<FFIBox<[u8]>, ()> {
122 todo!()
123 }
124
125 unsafe extern "C" fn deserialize_success(src: &[u8]) -> Result<AnySuccess, ()> {
126 todo!()
127 }
128
129 unsafe extern "C" fn deserialize_failure(src: &[u8]) -> Result<AnyFailure, ()> {
130 todo!()
131 }
132
133 fn operation_kind() -> &'static str {
134 todo!()
135 }
136 }

giterated-plugin/src/vtable/setting.rs

View file
@@ -0,0 +1,66 @@
1 use std::mem::transmute;
2
3 use giterated_models::settings::Setting;
4
5 use crate::FFIBox;
6
7 #[repr(C)]
8 pub struct NewAnySetting {
9 /// A pointer to the plugin-local object type. We are not capable of
10 /// knowing what this type is, we use the provided vtable.
11 inner: FFIBox<()>,
12 vtable: SettingVtable,
13 }
14
15 impl NewAnySetting {
16 pub fn new<V: IntoSettingVTable>(value: V) -> Self {
17 Self {
18 inner: FFIBox::from_box(Box::new(value)).untyped(),
19 vtable: SettingVtable::new::<V>(),
20 }
21 }
22
23 pub unsafe fn transmute_owned<T>(self) -> Box<T> {
24 Box::from_raw(self.inner.0 as *mut T)
25 }
26
27 pub unsafe fn transmute_ref<T>(&self) -> &T {
28 let ptr: *const T = transmute(self.inner.0);
29
30 ptr.as_ref().unwrap()
31 }
32 }
33
34 #[derive(Clone, Copy)]
35 #[repr(C)]
36 pub struct SettingVtable {
37 pub deserialize: unsafe extern "C" fn(&[u8]) -> Result<(), ()>,
38 pub serialize: unsafe extern "C" fn(NewAnySetting) -> Result<FFIBox<[u8]>, ()>,
39 }
40
41 impl SettingVtable {
42 pub fn new<T: IntoSettingVTable>() -> Self {
43 Self {
44 deserialize: T::deserialize,
45 serialize: T::serialize,
46 }
47 }
48 }
49
50 pub trait IntoSettingVTable {
51 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<(), ()>;
52 unsafe extern "C" fn serialize(this: NewAnySetting) -> Result<FFIBox<[u8]>, ()>;
53 }
54
55 impl<S> IntoSettingVTable for S
56 where
57 S: Setting,
58 {
59 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<(), ()> {
60 todo!()
61 }
62
63 unsafe extern "C" fn serialize(this: NewAnySetting) -> Result<FFIBox<[u8]>, ()> {
64 todo!()
65 }
66 }

giterated-plugin/src/vtable/value.rs

View file
@@ -0,0 +1,67 @@
1 use giterated_models::{object::GiteratedObject, value::GiteratedObjectValue};
2
3 use crate::FFIBox;
4
5 #[repr(C)]
6 pub struct NewAnyValue {
7 /// A pointer to the plugin-local object type. We are not capable of
8 /// knowing what this type is, we use the provided vtable.
9 inner: FFIBox<()>,
10 vtable: ValueVTable,
11 }
12
13 impl NewAnyValue {
14 pub fn new<O, V: IntoValueVTable<O>>(value: V) -> Self {
15 NewAnyValue {
16 inner: FFIBox::from_box(Box::new(value)).untyped(),
17 vtable: ValueVTable::new::<O, V>(),
18 }
19 }
20 }
21
22 #[derive(Clone, Copy)]
23 #[repr(C)]
24 pub struct ValueVTable {
25 pub deserialize: unsafe extern "C" fn(&[u8]) -> Result<NewAnyValue, ()>,
26 pub serialize: unsafe extern "C" fn(NewAnyValue) -> Result<FFIBox<[u8]>, ()>,
27 }
28
29 impl ValueVTable {
30 pub fn new<O, T: IntoValueVTable<O>>() -> Self {
31 Self {
32 deserialize: T::deserialize,
33 serialize: T::serialize,
34 }
35 }
36 }
37
38 pub trait IntoValueVTable<O> {
39 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<NewAnyValue, ()>;
40 unsafe extern "C" fn serialize(this: NewAnyValue) -> Result<FFIBox<[u8]>, ()>;
41 }
42
43 impl<O, V> IntoValueVTable<O> for V
44 where
45 O: GiteratedObject,
46 V: GiteratedObjectValue<Object = O>,
47 {
48 unsafe extern "C" fn deserialize(src: &[u8]) -> Result<NewAnyValue, ()> {
49 let _guard = trace_span!(
50 "deserialize value",
51 object = O::object_name(),
52 value = V::value_name()
53 );
54
55 trace!("Deserializing");
56 let deserialized: V = serde_json::from_slice(src).unwrap();
57
58 Ok(NewAnyValue {
59 inner: FFIBox::from_box(Box::new(deserialized)).untyped(),
60 vtable: ValueVTable::new::<O, V>(),
61 })
62 }
63
64 unsafe extern "C" fn serialize(this: NewAnyValue) -> Result<FFIBox<[u8]>, ()> {
65 todo!()
66 }
67 }

plugins/README.md

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

plugins/example-plugin/Cargo.toml

View file
@@ -0,0 +1,21 @@
1 [package]
2 name = "example-plugin"
3 version = "0.1.0"
4 edition = "2021"
5
6 [lib]
7 name = "example_plugin_dylib"
8 path = "src/lib.rs"
9 crate-type = ["dylib"]
10
11 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12
13 [dependencies]
14 giterated-plugin = { path = "../../giterated-plugin" }
15 giterated-plugin-sys = { path = "../../giterated-plugin/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"

plugins/example-plugin/src/lib.rs

View file
@@ -0,0 +1,95 @@
1 use std::sync::OnceLock;
2
3 use giterated_models::{
4 error::OperationError,
5 instance::Instance,
6 object::ObjectRequest,
7 user::{DisplayName, User},
8 };
9 use giterated_plugin::{
10 handle::PluginInitializationState,
11 new_stack::{FFIPluginMeta, PluginState},
12 vtable::{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);
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 // }

plugins/example-plugin/src/main.rs

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

plugins/giterated-backend/Cargo.toml

View file
@@ -0,0 +1,20 @@
1 [package]
2 name = "giterated-backend"
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/giterated-plugin-sys" }
17 toml = { version = "0.8" }
18 tracing = "0.1"
19 tracing-subscriber = "0.3"
20 serde_json = "1.0"

plugins/giterated-backend/src/handlers.rs

View file
@@ -0,0 +1,28 @@
1 use std::sync::Arc;
2
3 use giterated_models::{
4 error::{OperationError, RepositoryError, UserError},
5 repository::{Repository, RepositoryInfoRequest, RepositorySummary, RepositoryView},
6 user::{User, UserRepositoriesRequest},
7 };
8 use giterated_plugin::new_stack::{runtime_handler::RuntimeHandle, OperationState, Runtime, State};
9
10 use crate::DatabaseBackend;
11
12 pub async fn user_get_repositories(
13 state: DatabaseBackend,
14 object: User,
15 request: UserRepositoriesRequest,
16 ) -> Result<Vec<RepositorySummary>, OperationError<UserError>> {
17 todo!()
18 }
19
20 pub async fn repository_info(
21 state: DatabaseBackend,
22 object: Repository,
23 request: RepositoryInfoRequest,
24 runtime: RuntimeHandle,
25 State(operation_state): State<OperationState>,
26 ) -> Result<RepositoryView, OperationError<RepositoryError>> {
27 todo!()
28 }

plugins/giterated-backend/src/lib.rs

View file
@@ -0,0 +1,16 @@
1 pub mod handlers;
2 pub mod value;
3
4 use giterated_models::{instance::Instance, repository::Repository, user::User};
5 use giterated_plugin_sys::{PluginStackBuilder, ValueSettingExt};
6 use handlers::{repository_info, user_get_repositories};
7 use sqlx::PgPool;
8 use value::{user_get_bio, user_get_display_name, user_set_bio, user_set_display_name};
9
10 /// A backend implementation which attempts to resolve data from the instance's database.
11 #[derive(Debug, Clone)]
12 #[allow(unused)]
13 pub struct DatabaseBackend {
14 pub(self) our_instance: Instance,
15 pub(self) pool: PgPool,
16 }

plugins/giterated-backend/src/value.rs

View file
@@ -0,0 +1,36 @@
1 use giterated_models::{
2 error::OperationError,
3 user::{Bio, DisplayName, User},
4 };
5
6 use crate::DatabaseBackend;
7
8 pub async fn user_get_display_name(
9 state: DatabaseBackend,
10 user: User,
11 ) -> Result<DisplayName, OperationError<anyhow::Error>> {
12 todo!()
13 }
14
15 pub async fn user_set_display_name(
16 state: DatabaseBackend,
17 user: User,
18 display_name: DisplayName,
19 ) -> Result<(), OperationError<anyhow::Error>> {
20 todo!()
21 }
22
23 pub async fn user_get_bio(
24 state: DatabaseBackend,
25 user: User,
26 ) -> Result<Bio, OperationError<anyhow::Error>> {
27 todo!()
28 }
29
30 pub async fn user_set_bio(
31 state: DatabaseBackend,
32 user: User,
33 bio: Bio,
34 ) -> Result<(), OperationError<anyhow::Error>> {
35 todo!()
36 }

plugins/giterated-issues/Cargo.toml

View file
@@ -0,0 +1,25 @@
1 [package]
2 name = "giterated-issues"
3 version = "0.1.0"
4 edition = "2021"
5
6 [lib]
7 name = "giterated_issues"
8 path = "src/lib.rs"
9 crate-type = ["rlib", "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-models = { path = "../../giterated-models" }
16 serde = { version = "1.0", features = [ "derive" ]}
17 anyhow = "1"
18 thiserror = "1"
19 sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native-tls", "postgres", "macros", "migrate", "chrono" ] }
20 tokio = { version = "1.32", features = [ "full" ] }
21 giterated-plugin-sys = { path = "../../giterated-plugin/giterated-plugin-sys" }
22 toml = { version = "0.8" }
23 tracing = "0.1"
24 tracing-subscriber = "0.3"
25 serde_json = "1.0"

plugins/giterated-issues/src/db.rs

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

plugins/giterated-issues/src/handlers.rs

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

plugins/giterated-issues/src/lib.rs

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

plugins/giterated-issues/src/main.rs

View file
@@ -0,0 +1,45 @@
1 use std::str::FromStr;
2
3 use giterated_issues::operations::CreateIssueRequest;
4 use giterated_models::{
5 instance::Instance,
6 object::{GiteratedObject, ObjectRequest},
7 operation::GiteratedOperation,
8 repository::Repository,
9 user::User,
10 };
11 use giterated_plugin::{handle::PluginHandle, new_stack::Runtime};
12 use tracing::Level;
13
14 #[tokio::main]
15 pub async fn main() {
16 tracing_subscriber::fmt()
17 .pretty()
18 .with_thread_names(true)
19 .with_max_level(Level::TRACE)
20 .init();
21
22 let mut handle = PluginHandle::from_dylib("giterated_issues.dll").unwrap();
23
24 let mut runtime = Runtime::<()>::new();
25
26 runtime.insert_plugin(handle);
27
28 let operation = CreateIssueRequest {
29 name: String::from("test issue"),
30 contents: String::from("hey!"),
31 author: User::from_str("amber:giterated.dev").unwrap(),
32 };
33
34 match runtime.handle_typed(
35 Repository::from_str("barson:giterated.dev/[email protected]").unwrap(),
36 operation,
37 ) {
38 Ok(success) => {
39 println!("Success in create issue: {:?}", success)
40 }
41 Err(err) => {
42 println!("Error in create issue: {:?}", err)
43 }
44 }
45 }

plugins/giterated-issues/src/operations.rs

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

plugins/giterated-issues/src/setting.rs

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

plugins/giterated-issues/src/value.rs

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

plugins/giterated-protocol/Cargo.toml

View file
@@ -0,0 +1,30 @@
1 [package]
2 name = "giterated-protocol"
3 version = "0.1.0"
4 edition = "2021"
5
6 [lib]
7 name = "giterated_protocol"
8 path = "src/lib.rs"
9 crate-type = ["dylib", "rlib"]
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-models = { path = "../../giterated-models" }
16 serde = { version = "1.0", features = [ "derive" ]}
17 anyhow = "1"
18 giterated-plugin-sys = { path = "../../giterated-plugin/giterated-plugin-sys" }
19 toml = { version = "0.8" }
20 tracing = "0.1"
21 tracing-subscriber = "0.3"
22 serde_json = "1.0"
23 rand = "0.8"
24 rsa = {version = "0.9", features = ["sha2"]}
25 tokio-tungstenite = { version = "0.20" }
26 tokio = { version = "1.32.0", features = ["full"] }
27 thiserror = "1"
28 bincode = "1.3"
29 futures-util = "0.3"
30 async-trait = "0.1"
30 \ No newline at end of file

plugins/giterated-protocol/src/handlers.rs

View file
@@ -0,0 +1,255 @@
1 use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc};
2
3 use anyhow::Error;
4 use futures_util::{SinkExt, StreamExt};
5 use giterated_models::{
6 error::{NetworkOperationError, OperationError},
7 instance::Instance,
8 object::GiteratedObject,
9 operation::GiteratedOperation,
10 };
11 use giterated_plugin::{
12 new_stack::{runtime_handler::RuntimeHandle, Runtime},
13 AnyFailure, AnyObject, AnyOperation, AnySuccess,
14 };
15 use serde::{Deserialize, Serialize};
16 use tokio::net::TcpStream;
17 use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
18
19 use crate::{Authenticated, GiteratedMessage, ProtocolState, RemoteError};
20
21 pub async fn handle_network_operation<OS: Send + Sync + Clone + 'static>(
22 state: ProtocolState,
23 object: NetworkedObject,
24 operation: NetworkedOperation,
25 runtime: RuntimeHandle,
26 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
27 trace!("Handle network operation {}", operation.name);
28
29 runtime
30 .handle_serialized(&object.0, &operation.name, &operation.payload)
31 .await
32 }
33
34 #[derive(Clone, Debug, Serialize, Deserialize)]
35 pub struct NetworkedObject(pub String);
36
37 impl FromStr for NetworkedObject {
38 type Err = ();
39
40 fn from_str(s: &str) -> Result<Self, Self::Err> {
41 Ok(NetworkedObject(s.to_string()))
42 }
43 }
44
45 impl Display for NetworkedObject {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.write_str(&self.0)
48 }
49 }
50
51 impl GiteratedObject for NetworkedObject {
52 fn object_name() -> &'static str {
53 "networked_object"
54 }
55
56 fn from_object_str(_object_str: &str) -> Result<Self, Error> {
57 todo!()
58 }
59
60 fn home_uri(&self) -> String {
61 todo!()
62 }
63 }
64
65 #[derive(Clone, Debug, Serialize, Deserialize)]
66 pub struct NetworkedOperation {
67 pub name: String,
68 pub payload: Vec<u8>,
69 }
70
71 impl NetworkedOperation {
72 pub fn new(name: String, payload: Vec<u8>) -> Self {
73 Self { name, payload }
74 }
75 }
76
77 impl GiteratedOperation<NetworkedObject> for NetworkedOperation {
78 type Success = Vec<u8>;
79
80 type Failure = Vec<u8>;
81
82 fn operation_name() -> &'static str {
83 "networked_operation"
84 }
85 }
86
87 /// Handler which will attempt to resolve any operation that doesn't resolve locally
88 /// against a remote instance.
89 pub async fn try_handle_with_remote<OS>(
90 state: ProtocolState,
91 object: AnyObject,
92 operation: AnyOperation,
93 ) -> Result<AnySuccess, OperationError<AnyFailure>> {
94 // if object.is::<NetworkedObject>() {
95 // return Err(OperationError::Unhandled);
96 // }
97 // trace!(
98 // "Try handling object operation {}::{} with remote",
99 // object.kind(),
100 // operation.kind().operation_name
101 // );
102 // TODO:
103 // Ideally we support pass-through on object types that aren't used locally.
104 // For now, we aren't worrying about that.
105 let object_meta = object.vtable().clone();
106
107 let operation_meta = operation.vtable().clone();
108
109 // trace!(
110 // "Serializing with {}::{}",
111 // operation.kind().object_name,
112 // operation.kind().operation_name
113 // );
114
115 let object_home_uri = unsafe { (object_meta.home_uri)(&object) };
116
117 if let Some(home_uri) = state.home_uri {
118 if &home_uri == object_home_uri.as_ref() {
119 // This isn't a remote request, requests aren't supposed to hit this layer
120 // if they're not remote.
121 // warn!("Try handling object operation {}::{}, resolved object home uri as local home uri. This is a bug.", object.kind(),
122 // operation.kind().operation_name);
123
124 return Err(OperationError::Unhandled);
125 }
126 }
127
128 // trace!(
129 // "Handling object operation {}::{} sending payload",
130 // object.kind(),
131 // operation.kind().operation_name
132 // );
133
134 let object = NetworkedObject(unsafe { (object_meta.to_str)(object).as_ref().to_string() });
135
136 let payload = unsafe { (operation_meta.serialize)(&operation) }.unwrap();
137 let payload = Vec::from(payload.as_ref());
138
139 let operation = NetworkedOperation::new(operation_meta.kind().to_string(), payload);
140
141 // let authenticated = Authenticated::new(object, operation);
142
143 let message = GiteratedMessage {
144 object,
145 operation: NetworkedOperation::operation_name().to_string(),
146 payload: operation,
147 };
148
149 let authenticated = Authenticated::new(message);
150
151 let mut socket: WebSocketStream<MaybeTlsStream<TcpStream>> = connect_to(
152 &Instance::from_str(&object_home_uri).unwrap(),
153 &Some(("127.0.0.1:1111").parse().unwrap()),
154 )
155 .await
156 .unwrap();
157
158 // TODO AUTH
159
160 let result: Result<Vec<u8>, OperationError<Vec<u8>>> =
161 send_expect(&mut socket, authenticated).await;
162
163 match result {
164 Ok(success) => {
165 let success = unsafe { (operation_meta.deserialize_success)(&success) }.unwrap();
166
167 Ok(success)
168 }
169 Err(err) => Err(match err {
170 OperationError::Operation(failure) => {
171 let failure = unsafe { (operation_meta.deserialize_failure)(&failure) }.unwrap();
172
173 OperationError::Operation(failure)
174 }
175 OperationError::Internal(internal) => OperationError::Internal(internal),
176 OperationError::Unhandled => OperationError::Unhandled,
177 }),
178 }
179 }
180
181 type Socket = WebSocketStream<MaybeTlsStream<TcpStream>>;
182
183 async fn connect_to(
184 instance: &Instance,
185
186 socket_addr: &Option<SocketAddr>,
187 ) -> Result<Socket, Error> {
188 if let Some(addr) = socket_addr {
189 info!(
190 "Connecting to {}",
191 format!("ws://{}/.giterated/daemon/", addr)
192 );
193
194 let (websocket, _response) =
195 connect_async(&format!("ws://{}/.giterated/daemon/", addr)).await?;
196
197 info!("Connection established with {}", addr);
198
199 Ok(websocket)
200 } else {
201 info!(
202 "Connecting to {}",
203 format!("wss://{}/.giterated/daemon/", instance.0)
204 );
205
206 let (websocket, _response) =
207 connect_async(&format!("wss://{}/.giterated/daemon/", instance.0)).await?;
208
209 info!("Connection established with {}", instance.0);
210
211 Ok(websocket)
212 }
213 }
214
215 async fn send_expect<O: GiteratedObject, D: GiteratedOperation<O>>(
216 socket: &mut Socket,
217 message: Authenticated<O, D>,
218 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
219 let payload = bincode::serialize(&message.into_payload()).unwrap();
220
221 socket.send(Message::Binary(payload)).await.unwrap();
222
223 while let Some(message) = socket.next().await {
224 let payload = match message.unwrap() {
225 Message::Binary(payload) => payload,
226
227 _ => {
228 continue;
229 }
230 };
231
232 let raw_result =
233 bincode::deserialize::<Result<Vec<u8>, NetworkOperationError<Vec<u8>>>>(&payload)
234 .map_err(|e| OperationError::Internal(Error::from(e)))?;
235
236 trace!(
237 "Received response for networked operation {}::{}.",
238 O::object_name(),
239 D::operation_name()
240 );
241
242 return match raw_result {
243 Ok(success) => Ok(success),
244 Err(err) => Err(match err {
245 NetworkOperationError::Operation(operation_error) => {
246 OperationError::Operation(operation_error)
247 }
248 NetworkOperationError::Internal => OperationError::Internal(RemoteError.into()),
249 NetworkOperationError::Unhandled => OperationError::Unhandled,
250 }),
251 };
252 }
253
254 panic!()
255 }

plugins/giterated-protocol/src/lib.rs

View file
@@ -0,0 +1,300 @@
1 use std::{ops::Deref, str::FromStr, sync::Arc};
2
3 use giterated_models::{
4 authenticated::{InstanceSignature, UserAuthenticationToken},
5 instance::Instance,
6 object::GiteratedObject,
7 operation::GiteratedOperation,
8 user::User,
9 };
10 use handlers::{NetworkedObject, NetworkedOperation};
11 use object::NetworkAnyObject;
12 use operations::NetworkAnyOperation;
13 use rsa::{
14 pkcs1::DecodeRsaPrivateKey,
15 pss::SigningKey,
16 sha2::Sha256,
17 signature::{RandomizedSigner, SignatureEncoding},
18 RsaPrivateKey,
19 };
20 use serde::{Deserialize, Serialize};
21 use std::fmt::Debug;
22
23 pub mod handlers;
24 pub mod object;
25 pub mod operations;
26
27 #[macro_use]
28 extern crate tracing;
29
30 #[derive(Clone)]
31 pub struct StackOperationState {
32 pub our_instance: Instance,
33 pub instance: Option<AuthenticatedInstance>,
34 pub user: Option<AuthenticatedUser>,
35 }
36
37 #[derive(Clone, Debug)]
38 pub struct AuthenticatedInstance(Instance);
39
40 impl AuthenticatedInstance {
41 pub fn new(instance: Instance) -> Self {
42 AuthenticatedInstance(instance)
43 }
44 }
45
46 impl Deref for AuthenticatedInstance {
47 type Target = Instance;
48
49 fn deref(&self) -> &Self::Target {
50 &self.0
51 }
52 }
53
54 #[derive(Clone, Debug)]
55 pub struct AuthenticatedUser(User);
56
57 impl AuthenticatedUser {
58 pub fn new(user: User) -> Self {
59 AuthenticatedUser(user)
60 }
61 }
62
63 impl Deref for AuthenticatedUser {
64 type Target = User;
65
66 fn deref(&self) -> &Self::Target {
67 &self.0
68 }
69 }
70
71 #[derive(Clone)]
72 pub struct NetworkOperationState {
73 authentication: Vec<Arc<dyn AuthenticationSourceProviders + Send + Sync>>,
74 }
75
76 impl NetworkOperationState {
77 pub fn new() -> Self {
78 Self {
79 authentication: vec![],
80 }
81 }
82
83 pub fn authenticate(
84 &mut self,
85 provider: impl AuthenticationSourceProviders + Send + Sync + 'static,
86 ) {
87 self.authentication.push(Arc::new(provider))
88 }
89 }
90
91 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
92 pub struct AuthenticatedPayload {
93 pub source: Vec<AuthenticationSource>,
94 pub object: String,
95 pub operation: String,
96 pub payload: Vec<u8>,
97 }
98
99 impl AuthenticatedPayload {
100 pub fn into_message(self) -> GiteratedMessage<NetworkedObject, NetworkedOperation> {
101 GiteratedMessage {
102 object: NetworkedObject::from_str(&self.object).unwrap(),
103 operation: self.operation,
104 payload: serde_json::from_slice(&self.payload).unwrap(),
105 }
106 }
107 }
108
109 pub trait AuthenticationSourceProvider: Debug {
110 fn authenticate(&self, payload: &Vec<u8>) -> AuthenticationSource;
111 }
112
113 pub trait AuthenticationSourceProviders: Debug {
114 fn authenticate_all(&self, payload: &Vec<u8>) -> Vec<AuthenticationSource>;
115 }
116
117 impl<A> AuthenticationSourceProviders for A
118 where
119 A: AuthenticationSourceProvider,
120 {
121 fn authenticate_all(&self, payload: &Vec<u8>) -> Vec<AuthenticationSource> {
122 vec![self.authenticate(payload)]
123 }
124 }
125
126 impl<A, B> AuthenticationSourceProviders for (A, B)
127 where
128 A: AuthenticationSourceProvider,
129 B: AuthenticationSourceProvider,
130 {
131 fn authenticate_all(&self, payload: &Vec<u8>) -> Vec<AuthenticationSource> {
132 let (first, second) = self;
133
134 vec![first.authenticate(payload), second.authenticate(payload)]
135 }
136 }
137
138 #[derive(Clone, Debug)]
139 pub struct UserAuthenticator {
140 pub user: User,
141 pub token: UserAuthenticationToken,
142 }
143
144 impl AuthenticationSourceProvider for UserAuthenticator {
145 fn authenticate(&self, _payload: &Vec<u8>) -> AuthenticationSource {
146 AuthenticationSource::User {
147 user: self.user.clone(),
148 token: self.token.clone(),
149 }
150 }
151 }
152
153 #[derive(Debug, Clone)]
154 pub struct InstanceAuthenticator {
155 pub instance: Instance,
156 pub private_key: String,
157 }
158
159 impl AuthenticationSourceProvider for InstanceAuthenticator {
160 fn authenticate(&self, payload: &Vec<u8>) -> AuthenticationSource {
161 let mut rng = rand::thread_rng();
162
163 let private_key = RsaPrivateKey::from_pkcs1_pem(&self.private_key).unwrap();
164 let signing_key = SigningKey::<Sha256>::new(private_key);
165 let signature = signing_key.sign_with_rng(&mut rng, payload);
166
167 AuthenticationSource::Instance {
168 instance: self.instance.clone(),
169 // TODO: Actually parse signature from private key
170 signature: InstanceSignature(signature.to_bytes().into_vec()),
171 }
172 }
173 }
174
175 #[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
176 pub enum AuthenticationSource {
177 User {
178 user: User,
179 token: UserAuthenticationToken,
180 },
181 Instance {
182 instance: Instance,
183 signature: InstanceSignature,
184 },
185 }
186
187 #[derive(Serialize)]
188 #[serde(bound(deserialize = "O: GiteratedObject, V: GiteratedOperation<O>"))]
189 pub struct GiteratedMessage<O: GiteratedObject, V: GiteratedOperation<O>> {
190 #[serde(with = "string")]
191 pub object: O,
192 pub operation: String,
193 pub payload: V,
194 }
195
196 #[allow(unused)]
197 mod string {
198 use std::fmt::Display;
199 use std::str::FromStr;
200
201 use serde::{de, Deserialize, Deserializer, Serializer};
202
203 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
204 where
205 T: Display,
206 S: Serializer,
207 {
208 serializer.collect_str(value)
209 }
210
211 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
212 where
213 T: FromStr,
214 T::Err: Display,
215 D: Deserializer<'de>,
216 {
217 String::deserialize(deserializer)?
218 .parse()
219 .map_err(de::Error::custom)
220 }
221 }
222
223 impl GiteratedMessage<NetworkAnyObject, NetworkAnyOperation> {
224 pub fn try_into<O: GiteratedObject, V: GiteratedOperation<O>>(
225 &self,
226 ) -> Result<GiteratedMessage<O, V>, ()> {
227 let object = O::from_object_str(&self.object.0).map_err(|_| ())?;
228 let payload = serde_json::from_slice::<V>(&self.payload.0).map_err(|_| ())?;
229
230 Ok(GiteratedMessage {
231 object,
232 operation: self.operation.clone(),
233 payload,
234 })
235 }
236 }
237
238 impl<V: GiteratedOperation<O> + Debug, O: GiteratedObject + Debug> Debug
239 for GiteratedMessage<O, V>
240 {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 f.debug_struct("GiteratedMessage")
243 .field("object", &self.object)
244 .field("operation", &self.operation)
245 .field("payload", &self.payload)
246 .finish()
247 }
248 }
249
250 #[derive(Debug, Clone, thiserror::Error)]
251 #[error("a remote internal error occurred")]
252 pub struct RemoteError;
253
254 #[derive(Debug, thiserror::Error)]
255 #[error("a remote internal error occurred")]
256
257 pub struct NetworkError;
258
259 #[derive(Debug)]
260 pub struct Authenticated<O: GiteratedObject, D: GiteratedOperation<O>> {
261 pub source: Vec<Arc<dyn AuthenticationSourceProviders + Send + Sync>>,
262 pub message: GiteratedMessage<O, D>,
263 }
264
265 impl<O: GiteratedObject, D: GiteratedOperation<O>> Authenticated<O, D> {
266 pub fn new(message: GiteratedMessage<O, D>) -> Self {
267 Self {
268 source: vec![],
269 message,
270 }
271 }
272
273 pub fn append_authentication(
274 &mut self,
275 authentication: Arc<dyn AuthenticationSourceProviders + Send + Sync>,
276 ) {
277 self.source.push(authentication);
278 }
279
280 pub fn into_payload(mut self) -> AuthenticatedPayload {
281 let payload = serde_json::to_vec(&self.message.payload).unwrap();
282
283 AuthenticatedPayload {
284 object: self.message.object.to_string(),
285 operation: self.message.operation,
286 source: self
287 .source
288 .drain(..)
289 .map(|provider| provider.as_ref().authenticate_all(&payload))
290 .flatten()
291 .collect::<Vec<_>>(),
292 payload,
293 }
294 }
295 }
296
297 #[derive(Clone, Debug)]
298 pub struct ProtocolState {
299 pub home_uri: Option<String>,
300 }

plugins/giterated-protocol/src/object.rs

View file
@@ -0,0 +1,38 @@
1 use std::{convert::Infallible, fmt::Display, str::FromStr};
2
3 use anyhow::Error;
4 use giterated_models::object::GiteratedObject;
5 use serde::{Deserialize, Serialize};
6
7 #[derive(Clone, Debug, Serialize, Deserialize)]
8 #[serde(transparent)]
9 #[repr(transparent)]
10 pub struct NetworkAnyObject(pub String);
11
12 impl GiteratedObject for NetworkAnyObject {
13 fn object_name() -> &'static str {
14 "network_object"
15 }
16
17 fn from_object_str(object_str: &str) -> Result<Self, Error> {
18 Ok(Self(object_str.to_string()))
19 }
20
21 fn home_uri(&self) -> String {
22 todo!()
23 }
24 }
25
26 impl Display for NetworkAnyObject {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.write_str(&self.0)
29 }
30 }
31
32 impl FromStr for NetworkAnyObject {
33 type Err = Infallible;
34
35 fn from_str(s: &str) -> Result<Self, Self::Err> {
36 Ok(Self(s.to_owned()))
37 }
38 }

plugins/giterated-protocol/src/operations.rs

View file
@@ -0,0 +1,13 @@
1 use giterated_models::{object::GiteratedObject, operation::GiteratedOperation};
2 use serde::{Deserialize, Serialize};
3
4 #[derive(Clone, Debug, Serialize, Deserialize)]
5 #[serde(transparent)]
6 #[repr(transparent)]
7 pub struct NetworkAnyOperation(pub Vec<u8>);
8
9 impl<O: GiteratedObject> GiteratedOperation<O> for NetworkAnyOperation {
10 type Success = Vec<u8>;
11
12 type Failure = Vec<u8>;
13 }