Fucking whatever there you go
parent: tbd commit: 57c2ca5
Showing 106 changed files with 3840 insertions and 9693 deletions
Cargo.lock
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 | } |