commit 6cf9e62f00f493a653c151adab0e1acd201cd916 Author: Paul Z Date: Fri Apr 28 18:55:41 2023 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f82c255 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +.env +graph.dat +graph.dat.bak diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1c1f626 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1444 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113713495a32dd0ab52baf5c10044725aa3aec00b31beda84218e469029b72a3" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "permission" +version = "0.1.0" +dependencies = [ + "dotenvy", + "log", + "pretty_env_logger", + "prost", + "serde", + "tokio", + "tonic", + "tonic-build", +] + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustix" +version = "0.37.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0661814f891c57c930a610266415528da53c4933e6dea5fb350cbfe048a9ece" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "rustls-pemfile", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b09e3ce --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "permission" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version="1.0", features=["derive"] } +tokio = { version = "1.27.0", features = ["full"] } +log = "0.4.17" +pretty_env_logger = "0.4.0" +dotenvy = "0.15.7" +tonic = { version="0.9.2", features=["tls"] } +prost = "0.11.9" + +[build-dependencies] +tonic-build = "0.9.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6672921 --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +fn main() { + tonic_build::configure() + .build_server(true) + .compile(&["proto/graph.proto"], &["proto"]) + .unwrap(); +} diff --git a/graph.dat b/graph.dat new file mode 100644 index 0000000..c4f5c07 --- /dev/null +++ b/graph.dat @@ -0,0 +1,10 @@ +[user:bob] + +[doc:foo] +read = [doc:foo#owner, user:bob] +owner = [user:alice] + +[user:oscar] + +[user:alice] + diff --git a/graph.dat.bak b/graph.dat.bak new file mode 100644 index 0000000..9dc7a90 --- /dev/null +++ b/graph.dat.bak @@ -0,0 +1,10 @@ +[user:bob] + +[doc:foo] +read = [user:bob, doc:foo#owner] +owner = [user:alice] + +[user:oscar] + +[user:alice] + diff --git a/proto/graph.proto b/proto/graph.proto new file mode 100644 index 0000000..4981f83 --- /dev/null +++ b/proto/graph.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; +package eu.zettoit.graph; + +service ObjectService{ + rpc Create(Object) returns (Empty); + rpc Delete(Object) returns (Empty); + rpc Exists(Object) returns (ExistsResponse); +} + +service RelationService { + rpc Create(Relation) returns (Empty); + rpc Delete(Relation) returns (Empty); + rpc Exists(Relation) returns (ExistsResponse); +} + +service QueryService { + // check if one object or objectset is related to another by a relation + rpc IsRelatedTo(Relation) returns (IsRelatedToResponse); + + // get all objects that are related to one object by a relation + rpc GetRelatedTo(Set) returns (GetRelatedToResponse); + + // get all objects that the given object has a relation with + rpc GetRelations(GetRelationsRequest) returns (GetRelationsResponse); +} + +message ExistsResponse { + bool exists = 1; +} + +message IsRelatedToResponse{ + bool related = 1; +} + +message GetRelatedToResponse{ + repeated Object objects = 1; +} + +message GetRelationsRequest{ + Object object = 1; + string relation = 2; +} +message GetRelationsResponse{ + repeated Object objects = 1; +} + +message Object{ + string namespace = 1; + string id = 2; +} +message Set{ + string namespace = 1; + string id = 2; + string relation = 3; +} + +message ObjectOrSet { + oneof object_or_set{ + Object object = 1; + Set set = 2; + }; +} + +message Relation{ + oneof src{ + Object src_obj = 1; + Set src_set = 2; + }; + Object dst = 3; + string relation = 4; +} + +message Empty{} diff --git a/proto/graph_permissions.proto b/proto/graph_permissions.proto new file mode 100644 index 0000000..652bcb4 --- /dev/null +++ b/proto/graph_permissions.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package eu.zettoit.graph; + +message Object{ + string namespace = 1; + string id = 2; +} +message ObjectSet{ + Object object = 1; + string relation = 2; +} + + +message Relation{ + oneof src{ + Object object = 1; + ObjectSet object_set = 2; + } + ObjectSet dst = 3; +} diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..2170e77 --- /dev/null +++ b/src/graph.rs @@ -0,0 +1,586 @@ +use std::{ + collections::{ + hash_map::{Iter, IterMut}, + BinaryHeap, HashMap, HashSet, + }, + hash::Hash, + ops::Deref, + sync::Arc, +}; + +use log::info; +use serde::{Deserialize, Serialize}; +use tokio::{ + fs::File, + io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, +}; + +#[derive(Default)] +pub struct Graph { + nodes: BidMap, + edges: BidThreeMap, + counter: u32, +} + +#[derive(Hash, PartialEq, Eq, Clone, Serialize, Deserialize, Debug)] +pub struct Object { + pub namespace: String, + pub id: String, +} + +#[derive(Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Debug)] +pub struct ObjectRef(pub u32); + +#[derive(PartialEq, Eq, Hash, Clone, Debug, Deserialize, Serialize)] +pub enum ObjectOrSet { + Object(ObjectRef), + Set((ObjectRef, Relation)), +} + +#[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] +pub struct Relation(String); + +#[derive(PartialEq, Eq, Clone, Hash, Serialize, Deserialize, Debug)] +pub struct ObjectRelation(pub ObjectRef, pub Relation); + +impl Object { + pub fn new(namespace: &str, id: &str) -> Self { + Self { + namespace: namespace.to_string(), + id: id.to_string(), + } + } +} + +impl ObjectOrSet { + pub fn object_ref(&self) -> &ObjectRef { + match self { + ObjectOrSet::Object(obj) => obj, + ObjectOrSet::Set((obj, _)) => obj, + } + } + pub fn relation(&self) -> Option<&Relation> { + match self { + ObjectOrSet::Object(_) => None, + ObjectOrSet::Set((_, rel)) => Some(rel), + } + } +} + +impl From for ObjectOrSet { + fn from(value: ObjectRef) -> Self { + Self::Object(value) + } +} + +impl From<(ObjectRef, &str)> for ObjectOrSet { + fn from(value: (ObjectRef, &str)) -> Self { + Self::Set((value.0, Relation::new(value.1))) + } +} + +impl Relation { + pub fn new(relation: &str) -> Self { + Self(relation.to_string()) + } +} +impl Deref for Relation { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<(ObjectRef, Relation)> for ObjectRelation { + fn from(value: (ObjectRef, Relation)) -> Self { + Self(value.0, value.1) + } +} +impl From<(ObjectRef, &str)> for ObjectRelation { + fn from(value: (ObjectRef, &str)) -> Self { + Self(value.0, Relation::new(value.1)) + } +} + +impl Graph { + pub fn get_node(&self, namespace: &str, id: &str) -> Option { + self.nodes.get_by_a(&Object::new(namespace, id)).cloned() + } + pub fn object_from_ref(&self, obj: &ObjectRef) -> Object { + self.nodes.get_by_b(obj).unwrap().clone() + } + pub fn add_node(&mut self, node: Object) -> ObjectRef { + let obj_ref = ObjectRef(self.counter); + self.nodes.insert(node, obj_ref); + self.counter += 1; + obj_ref + } + pub fn remove_node(&mut self, node: Object) { + let index = self.nodes.remove_by_a(&node); + if let Some(index) = index { + self.edges.remove_by_c(&index); + self.edges.get_by_a(&ObjectOrSet::Object(*index)); + //TODO: remove edges with ObjectOrSet::Set + } + } + + pub fn has_relation(&self, src: ObjectOrSet, dst: ObjectRelation) -> bool { + self.edges.has(&src, &dst.1, &dst.0) + } + pub fn add_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) { + self.edges.insert(src, dst.1, dst.0); + } + pub fn remove_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) { + self.edges.remove(&src, &dst.1, &dst.0); + } + + pub fn is_related_to( + &self, + src: impl Into, + dst: impl Into, + ) -> bool { + let src = src.into(); + let dst = dst.into(); + let mut dist: HashMap = HashMap::new(); + let mut q: BinaryHeap = BinaryHeap::new(); + + for neighbor in self + .edges + .get_by_a(&src) + .iter() + .flat_map(|(r, m)| m.iter().map(|x| ObjectRelation(**x, (**r).clone()))) + { + if neighbor == dst { + return true; + } + dist.insert(neighbor.clone(), 1); + q.push(ObjectRelationDist(1, neighbor.clone())); + } + + while let Some(ObjectRelationDist(node_dist, node)) = q.pop() { + let node_dist = node_dist + 1; + let node = ObjectOrSet::Set((node.0, node.1)); + for neighbor in self + .edges + .get_by_a(&node) + .iter() + .flat_map(|(r, m)| m.iter().map(|x| ObjectRelation(**x, (**r).clone()))) + { + if neighbor == dst { + return true; + } + if let Some(existing_node_dist) = dist.get(&neighbor) { + if *existing_node_dist < node_dist { + continue; + } + } + dist.insert(neighbor.clone(), node_dist); + q.push(ObjectRelationDist(node_dist, neighbor.clone())); + } + } + + false + } + pub fn related_to(&self, dst: ObjectRef, relation: Relation) -> HashSet { + let mut relation_sets = vec![]; + let mut relations: HashSet = HashSet::new(); + for obj in self.edges.get_by_cb(&dst, &relation) { + match obj { + ObjectOrSet::Object(obj) => { + relations.insert(*obj); + } + ObjectOrSet::Set(set) => relation_sets.push(set), + } + } + while let Some(set) = relation_sets.pop() { + for obj in self.edges.get_by_cb(&set.0, &set.1) { + match obj { + ObjectOrSet::Object(obj) => { + relations.insert(*obj); + } + ObjectOrSet::Set(set) => relation_sets.push(set), + } + } + } + relations + } + pub fn relations(&self, src: impl Into) -> HashSet { + let src: ObjectRelation = src.into(); + + let mut visited = HashSet::new(); + let mut relation_sets = vec![]; + let mut relations = HashSet::new(); + + for (rel, neighbors) in self.edges.get_by_a(&ObjectOrSet::Object(src.0)) { + for neighbor in neighbors { + if *rel == src.1 { + relations.insert(*neighbor); + } + relation_sets.push((rel, neighbor)); + } + } + + while let Some((rel, obj_ref)) = relation_sets.pop() { + if !visited.contains(&(rel, obj_ref)) { + for (rel, neighbors) in self + .edges + .get_by_a(&ObjectOrSet::Set((*obj_ref, (*rel).clone()))) + { + for neighbor in neighbors { + if *rel == src.1 { + relations.insert(*neighbor); + } + relation_sets.push((rel, neighbor)); + } + } + visited.insert((rel, obj_ref)); + } + } + + relations + } + + pub async fn to_file(&self, file: &mut File) { + info!("writing graph to file"); + for (obj, obj_ref) in self.nodes.iter() { + file.write_all(format!("[{}:{}]\n", &obj.namespace, &obj.id).as_bytes()) + .await + .unwrap(); + for (rel, arr) in self.edges.get_by_c(obj_ref.as_ref()) { + let arr = arr + .iter() + .filter_map(|x| { + let obj_ref = x.object_ref(); + self.nodes.get_by_b(obj_ref).map(|obj| { + let (namespace, id) = (&obj.namespace, &obj.id); + + match x.relation() { + None => format!("{}:{}", &namespace, &id), + Some(rel) => format!("{}:{}#{}", &namespace, &id, &rel.0), + } + }) + }) + .reduce(|acc, e| acc + ", " + &e) + .unwrap_or_default(); + file.write_all(format!("{} = [{}]\n", &rel.0, &arr).as_bytes()) + .await + .unwrap(); + } + file.write_all("\n".as_bytes()).await.unwrap(); + } + } + + pub async fn from_file(file: &mut File) -> Self { + info!("reading graph from file"); + let reader = BufReader::new(file); + let mut lines = reader.lines(); + let mut graph = Graph::default(); + + let mut node: Option = None; + let mut relations = vec![]; + while let Ok(Some(line)) = lines.next_line().await { + if line.starts_with('[') && line.ends_with(']') { + let line = &mut line[1..line.len() - 1].split(':'); + let obj_ref = + graph.add_node(Object::new(line.next().unwrap(), line.next().unwrap())); + node = Some(obj_ref); + } else if line.contains('=') && line.contains('[') && line.contains(']') { + if let Some(dst) = node { + let equals_pos = line.find('=').unwrap(); + let arr_start = line.find('[').unwrap(); + let arr_stop = line.find(']').unwrap(); + + let rel = line[..equals_pos].trim(); + let arr = line[arr_start + 1..arr_stop].split(", "); + + for obj in arr { + let (src_namespace, src_id, src_rel) = if obj.contains('#') { + let sep_1 = obj.find(':').unwrap(); + let sep_2 = obj.find('#').unwrap(); + + let namespace = &obj[..sep_1]; + let id = &obj[sep_1 + 1..sep_2]; + let rel = &obj[sep_2 + 1..]; + + (namespace, id, Some(rel)) + } else { + let sep_1 = obj.find(':').unwrap(); + + let namespace = &obj[..sep_1]; + let id = &obj[sep_1 + 1..]; + + (namespace, id, None) + }; + + relations.push(( + src_namespace.to_string(), + src_id.to_string(), + src_rel.map(String::from), + dst, + rel.to_string(), + )); + } + } + } + } + + for relation in relations { + let src = match relation.2 { + Some(rel) => { + let obj = graph.get_node(&relation.0, &relation.1).unwrap(); + ObjectOrSet::Set((obj, Relation::new(&rel))) + } + None => { + let obj = graph.get_node(&relation.0, &relation.1).unwrap(); + ObjectOrSet::Object(obj) + } + }; + graph.add_relation(src, ObjectRelation(relation.3, Relation(relation.4))); + } + + graph + } +} + +/// Helper Struct used for Dijkstra +#[derive(PartialEq, Eq)] +struct ObjectRelationDist(u32, ObjectRelation); + +impl Ord for ObjectRelationDist { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + other.0.cmp(&self.0) + } +} +impl PartialOrd for ObjectRelationDist { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.0.cmp(&self.0)) + } +} + +pub struct BidMap { + left_to_right: HashMap, Arc>, + right_to_left: HashMap, Arc>, +} + +impl Default for BidMap { + fn default() -> Self { + Self { + left_to_right: Default::default(), + right_to_left: Default::default(), + } + } +} + +impl BidMap +where + A: Eq + Hash, + B: Eq + Hash, +{ + pub fn new() -> Self { + Self { + left_to_right: HashMap::new(), + right_to_left: HashMap::new(), + } + } + + pub fn insert(&mut self, a: A, b: B) { + let a = Arc::new(a); + let b = Arc::new(b); + self.left_to_right.insert(a.clone(), b.clone()); + self.right_to_left.insert(b, a); + } + + pub fn remove_by_a(&mut self, a: &A) -> Option> { + if let Some(b) = self.left_to_right.remove(a) { + self.right_to_left.remove(&b); + Some(b) + } else { + None + } + } + + pub fn remove_by_b(&mut self, b: &B) -> Option> { + if let Some(a) = self.right_to_left.remove(b) { + self.left_to_right.remove(&a); + Some(a) + } else { + None + } + } + + pub fn get_by_a(&self, a: &A) -> Option<&B> { + self.left_to_right.get(a).map(Deref::deref) + } + + pub fn get_by_b(&self, b: &B) -> Option<&A> { + self.right_to_left.get(b).map(Deref::deref) + } + + pub fn iter(&self) -> Iter, Arc> { + self.left_to_right.iter() + } + + pub fn iter_mut(&mut self) -> IterMut, Arc> { + self.left_to_right.iter_mut() + } +} + +pub struct BidThreeMap { + left_to_right: HashMap, HashMap, HashSet>>>, + right_to_left: HashMap, HashMap, HashSet>>>, +} + +impl BidThreeMap +where + A: Eq + Hash, + B: Eq + Hash, + C: Eq + Hash, +{ + pub fn new() -> Self { + Self { + left_to_right: HashMap::new(), + right_to_left: HashMap::new(), + } + } + + pub fn insert(&mut self, a: A, b: B, c: C) { + let a = Arc::new(a); + let b = Arc::new(b); + let c = Arc::new(c); + + if let Some(middle) = self.left_to_right.get_mut(&a) { + if let Some(right) = middle.get_mut(&b) { + right.insert(c.clone()); + } else { + let mut right = HashSet::new(); + right.insert(c.clone()); + middle.insert(b.clone(), right); + } + } else { + let mut middle = HashMap::new(); + let mut right = HashSet::new(); + right.insert(c.clone()); + middle.insert(b.clone(), right); + self.left_to_right.insert(a.clone(), middle); + } + + if let Some(middle) = self.right_to_left.get_mut(&c) { + if let Some(left) = middle.get_mut(&b) { + left.insert(a); + } else { + let mut left = HashSet::new(); + left.insert(a); + middle.insert(b, left); + } + } else { + let mut middle = HashMap::new(); + let mut left = HashSet::new(); + left.insert(a); + middle.insert(b, left); + self.right_to_left.insert(c, middle); + } + } + + pub fn remove(&mut self, a: &A, b: &B, c: &C) { + if let Some(right) = self.left_to_right.get_mut(a).and_then(|ltr| ltr.get_mut(b)) { + right.remove(c); + } + if let Some(left) = self.right_to_left.get_mut(c).and_then(|rtl| rtl.get_mut(b)) { + left.remove(a); + } + } + + pub fn remove_by_a(&mut self, a: &A) { + if let Some(map) = self.left_to_right.remove(a) { + for (b, set) in map { + for c in set { + if let Some(set) = self + .right_to_left + .get_mut(&c) + .and_then(|ltr| ltr.get_mut(&b)) + { + set.remove(a); + } + } + } + } + } + + pub fn remove_by_c(&mut self, c: &C) { + if let Some(map) = self.right_to_left.remove(c) { + for (b, set) in map { + for a in set { + if let Some(set) = self + .left_to_right + .get_mut(&a) + .and_then(|ltr| ltr.get_mut(&b)) + { + set.remove(c); + } + } + } + } + } + + pub fn has(&self, a: &A, b: &B, c: &C) -> bool { + self.left_to_right + .get(a) + .and_then(|ltr| ltr.get(b)) + .and_then(|ltr| ltr.get(c)) + .is_some() + } + + pub fn get_by_ab(&self, a: &A, b: &B) -> HashSet<&C> { + self.left_to_right + .get(a) + .and_then(|ltr| ltr.get(b)) + .map(|ltr| ltr.iter().map(|x| x.as_ref()).collect::>()) + .unwrap_or_default() + } + + pub fn get_by_cb(&self, c: &C, b: &B) -> HashSet<&A> { + self.right_to_left + .get(c) + .and_then(|rtl| rtl.get(b)) + .map(|rtl| rtl.iter().map(|x| x.as_ref()).collect::>()) + .unwrap_or_default() + } + + pub fn get_by_a(&self, a: &A) -> HashMap<&B, HashSet<&C>> { + self.left_to_right + .get(a) + .iter() + .flat_map(|x| x.iter()) + .map(|(b, c)| { + ( + b.as_ref(), + c.iter().map(|x| x.as_ref()).collect::>(), + ) + }) + .collect::<_>() + } + + pub fn get_by_c(&self, c: &C) -> HashMap<&B, HashSet<&A>> { + self.right_to_left + .get(c) + .iter() + .flat_map(|x| x.iter()) + .map(|(b, a)| { + ( + b.as_ref(), + a.iter().map(|x| x.as_ref()).collect::>(), + ) + }) + .collect::<_>() + } +} + +impl Default for BidThreeMap { + fn default() -> Self { + Self { + left_to_right: Default::default(), + right_to_left: Default::default(), + } + } +} diff --git a/src/graph_permissions.rs b/src/graph_permissions.rs new file mode 100644 index 0000000..4af7974 --- /dev/null +++ b/src/graph_permissions.rs @@ -0,0 +1 @@ +tonic::include_proto!("eu.zettoit.graph"); diff --git a/src/grpc_service.rs b/src/grpc_service.rs new file mode 100644 index 0000000..4c26440 --- /dev/null +++ b/src/grpc_service.rs @@ -0,0 +1,230 @@ +use std::sync::Arc; + +use log::info; +use tokio::sync::mpsc::Sender; +use tokio::sync::Mutex; +use tonic::{Request, Response, Status}; + +use crate::graph::{self, Graph, ObjectRelation}; +use crate::graph_permissions::{ + object_service_server::ObjectService, query_service_server::QueryService, relation::Src, + relation_service_server::RelationService, Empty, ExistsResponse, GetRelatedToResponse, + GetRelationsRequest, GetRelationsResponse, IsRelatedToResponse, Object, Relation, Set, +}; + +#[derive(Clone)] +pub struct GraphService { + pub graph: Arc>, + pub save_trigger: Sender<()>, +} + +#[tonic::async_trait] +impl ObjectService for GraphService { + async fn create(&self, request: Request) -> Result, Status> { + let mut graph = self.graph.lock().await; + + if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() { + return Err(Status::invalid_argument("namespace and id must be set")); + } + + graph.add_node(graph::Object::new( + &request.get_ref().namespace, + &request.get_ref().id, + )); + + info!( + "created object {}:{}", + &request.get_ref().namespace, + &request.get_ref().id + ); + + self.save_trigger.send(()).await.unwrap(); + + Ok(Response::new(Empty {})) + } + async fn delete(&self, request: Request) -> Result, Status> { + let mut graph = self.graph.lock().await; + + if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() { + return Err(Status::invalid_argument("namespace and id must be set")); + } + + graph.remove_node(graph::Object::new( + &request.get_ref().namespace, + &request.get_ref().id, + )); + + info!( + "removed object {}:{}", + &request.get_ref().namespace, + &request.get_ref().id + ); + + self.save_trigger.send(()).await.unwrap(); + + Ok(Response::new(Empty {})) + } + async fn exists(&self, request: Request) -> Result, Status> { + let graph = self.graph.lock().await; + + if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() { + return Err(Status::invalid_argument("namespace and id must be set")); + } + + let exists = graph + .get_node(&request.get_ref().namespace, &request.get_ref().id) + .is_some(); + + Ok(Response::new(ExistsResponse { exists })) + } +} + +#[tonic::async_trait] +impl RelationService for GraphService { + async fn create(&self, request: Request) -> Result, Status> { + let mut graph = self.graph.lock().await; + + let (src, dst) = transform_relation(request.get_ref(), &graph)?; + + graph.add_relation(src, dst); + + info!("created relation"); + + self.save_trigger.send(()).await.unwrap(); + + Ok(Response::new(Empty {})) + } + async fn delete(&self, request: Request) -> Result, Status> { + let mut graph = self.graph.lock().await; + + let (src, dst) = transform_relation(request.get_ref(), &graph)?; + + graph.remove_relation(src, dst); + + info!("removed relation relation"); + + self.save_trigger.send(()).await.unwrap(); + + Ok(Response::new(Empty {})) + } + async fn exists(&self, request: Request) -> Result, Status> { + let graph = self.graph.lock().await; + + let (src, dst) = transform_relation(request.get_ref(), &graph)?; + + let exists = graph.has_relation(src, dst); + + Ok(Response::new(ExistsResponse { exists })) + } +} + +#[tonic::async_trait] +impl QueryService for GraphService { + async fn is_related_to( + &self, + request: Request, + ) -> Result, Status> { + let graph = self.graph.lock().await; + + let (src, dst) = transform_relation(request.get_ref(), &graph)?; + + let related = graph.is_related_to(src, dst); + + Ok(Response::new(IsRelatedToResponse { related })) + } + async fn get_related_to( + &self, + request: Request, + ) -> Result, Status> { + let graph = self.graph.lock().await; + + let obj = graph + .get_node(&request.get_ref().namespace, &request.get_ref().id) + .ok_or(Status::not_found("object not found"))?; + let rel = graph::Relation::new(&request.get_ref().relation); + + Ok(Response::new(GetRelatedToResponse { + objects: graph + .related_to(obj, rel) + .into_iter() + .map(|x| { + let obj = graph.object_from_ref(&x); + Object { + namespace: obj.namespace.to_string(), + id: obj.id, + } + }) + .collect::>(), + })) + } + async fn get_relations( + &self, + request: Request, + ) -> Result, Status> { + let graph = self.graph.lock().await; + + if request.get_ref().relation.is_empty() { + return Err(Status::invalid_argument("relation must be set")); + } + + let obj = request + .get_ref() + .object + .as_ref() + .ok_or(Status::invalid_argument("object must be set"))?; + let obj = graph + .get_node(&obj.namespace, &obj.id) + .ok_or(Status::not_found("object not found"))?; + + Ok(Response::new(GetRelationsResponse { + objects: graph + .relations(ObjectRelation( + obj, + graph::Relation::new(&request.get_ref().relation), + )) + .into_iter() + .map(|x| { + let obj = graph.object_from_ref(&x); + Object { + namespace: obj.namespace.to_string(), + id: obj.id, + } + }) + .collect::>(), + })) + } +} + +fn transform_relation( + rel: &Relation, + graph: &Graph, +) -> Result<(graph::ObjectOrSet, graph::ObjectRelation), Status> { + let src = match rel + .src + .as_ref() + .ok_or(Status::invalid_argument("src must be set"))? + { + Src::SrcObj(object) => graph::ObjectOrSet::Object( + graph + .get_node(&object.namespace, &object.id) + .ok_or(Status::not_found("src object could not be found"))?, + ), + Src::SrcSet(set) => graph::ObjectOrSet::Set(( + graph + .get_node(&set.namespace, &set.id) + .ok_or(Status::not_found("src object could not be found"))?, + graph::Relation::new(&set.relation), + )), + }; + + let dst = rel + .dst + .as_ref() + .ok_or(Status::invalid_argument("dst must be set"))?; + let dst = graph + .get_node(&dst.namespace, &dst.id) + .ok_or(Status::not_found("dst object could not be found"))?; + let dst = ObjectRelation(dst, graph::Relation::new(&rel.relation)); + + Ok((src, dst)) +} diff --git a/src/kafka_backend.rs b/src/kafka_backend.rs new file mode 100644 index 0000000..a0f77e3 --- /dev/null +++ b/src/kafka_backend.rs @@ -0,0 +1,145 @@ +use std::{sync::Arc, time::Duration}; + +use kafka::{ + consumer::{Consumer, FetchOffset}, + producer::{Producer, Record, RequiredAcks}, +}; +use log::debug; +use serde::{Deserialize, Serialize}; +use tokio::{ + runtime::Runtime, + sync::{mpsc, RwLock}, + task::JoinHandle, +}; + +use crate::{ + graph::{Graph, ObjectRelation}, + object::{Object, ObjectOrSet, ObjectRef}, +}; + +#[derive(Serialize, Deserialize, Debug)] +pub enum Event { + AddObject(Object), + RemoveObject(Object), + AddRelation((ObjectOrSet, ObjectRelation)), + RemoveRelation((ObjectOrSet, ObjectRelation)), +} + +pub struct GraphProxy { + graph: Arc>, + producer_thread: JoinHandle<()>, + producer_tx: mpsc::Sender, + consumer_thread: JoinHandle<()>, +} +impl GraphProxy { + pub async fn run() -> Self { + let graph = Arc::new(RwLock::new(Graph::default())); + let (producer_tx, mut producer_rx) = mpsc::channel(1024); + + let mut producer = Producer::from_hosts(vec!["localhost:9092".to_owned()]) + .with_ack_timeout(Duration::from_secs(1)) + .with_required_acks(RequiredAcks::One) + .create() + .unwrap(); + + let producer_thread = tokio::spawn(async move { + loop { + if let Some(event) = producer_rx.recv().await { + let ser_event = serde_cbor::to_vec(&event).unwrap(); + producer + .send(&Record::from_value("gpm", ser_event)) + .unwrap(); + debug!("emitted Event: {:?}", event); + } + } + }); + + let mut consumer = Consumer::from_hosts(vec!["localhost:9092".to_string()]) + .with_client_id("gpm_dev".to_string()) + .with_topic("gpm".to_string()) + .with_fallback_offset(FetchOffset::Earliest) + .create() + .unwrap(); + let consumer_graph = graph.clone(); + let consumer_thread = tokio::task::spawn_blocking(move || { + let runtime = Runtime::new().unwrap(); + loop { + for msg_sets in consumer.poll().unwrap().iter() { + for msg in msg_sets.messages() { + let event: Event = serde_cbor::from_slice(msg.value).unwrap(); + debug!("received Event: {:?}", event); + let mut graph = runtime.block_on(consumer_graph.write()); + match event { + Event::AddObject(obj) => { + graph.add_node(obj); + } + Event::RemoveObject(obj) => { + graph.remove_node(obj); + } + Event::AddRelation((src, dst)) => { + graph.add_relation(src, dst); + } + Event::RemoveRelation((src, dst)) => { + graph.remove_relation(src, dst); + } + }; + } + consumer.consume_messageset(msg_sets).unwrap(); + } + consumer.commit_consumed().unwrap(); + } + }); + + Self { + graph, + producer_thread, + producer_tx, + consumer_thread, + } + } + + pub fn stop(&mut self) { + self.producer_thread.abort(); + self.consumer_thread.abort(); + } + + pub async fn add_node(&mut self, node: Object) { + self.producer_tx.send(Event::AddObject(node)).await.unwrap(); + } + pub async fn remove_node(&mut self, node: Object) { + self.producer_tx + .send(Event::RemoveObject(node)) + .await + .unwrap(); + } + pub async fn add_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) { + self.producer_tx + .send(Event::AddRelation((src, dst))) + .await + .unwrap(); + } + pub async fn remove_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) { + self.producer_tx + .send(Event::RemoveRelation((src, dst))) + .await + .unwrap(); + } + + pub async fn get_node(&self, namespace: &str, id: &str) -> Option { + let graph = self.graph.read().await; + graph.get_node(namespace, id) + } + + pub async fn is_related_to( + &self, + src: impl Into, + dst: impl Into, + ) -> bool { + let graph = self.graph.read().await; + graph.is_related_to(src, dst) + } + pub async fn related_by(&self, src: impl Into) -> Vec { + let graph = self.graph.read().await; + graph.related_by(src) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..19f9131 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,62 @@ +use std::{sync::Arc, time::Duration}; + +use graph::Graph; +use grpc_service::GraphService; +use tokio::{ + fs::{self, File}, + select, + sync::{mpsc::channel, Mutex}, +}; +use tonic::transport::Server; + +pub mod graph; +pub mod graph_permissions; +pub mod grpc_service; + +use crate::graph_permissions::{ + object_service_server::ObjectServiceServer, query_service_server::QueryServiceServer, + relation_service_server::RelationServiceServer, +}; + +#[tokio::main] +async fn main() { + dotenvy::dotenv().ok(); + pretty_env_logger::init(); + + let graph = if let Ok(mut file) = File::open("graph.dat").await { + Graph::from_file(&mut file).await + } else { + Graph::default() + }; + + let graph = Arc::new(Mutex::new(graph)); + + let (save_tx, mut save_rx) = channel::<()>(32); + let save_thread_graph = graph.clone(); + tokio::spawn(async move { + loop { + select! { + _ = tokio::time::sleep(Duration::from_secs(30)) => {} + _ = save_rx.recv() => {} + }; + let graph = save_thread_graph.lock().await; + + let _ = fs::copy("graph.dat", "graph.dat.bak").await; + let mut file = File::create("graph.dat").await.unwrap(); + graph.to_file(&mut file).await; + } + }); + + let graph_service = GraphService { + graph: graph.clone(), + save_trigger: save_tx.clone(), + }; + + Server::builder() + .add_service(ObjectServiceServer::new(graph_service.clone())) + .add_service(RelationServiceServer::new(graph_service.clone())) + .add_service(QueryServiceServer::new(graph_service)) + .serve("0.0.0.0:50051".parse().unwrap()) + .await + .unwrap() +}