diff --git a/.cargo/config.toml b/.cargo/config.toml index 578d0c9f6..ed8d8fe65 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,6 @@ [alias] -prisma = "run -p prisma-cli --" +prisma = "run -p prisma-cli --bin prisma --" +prisma-sync = "run -p prisma-cli --bin sync --" [target.x86_64-apple-darwin] rustflags = [ diff --git a/.github/actions/generate-prisma-client/action.yaml b/.github/actions/generate-prisma-client/action.yaml index 056afc346..01fd6a689 100644 --- a/.github/actions/generate-prisma-client/action.yaml +++ b/.github/actions/generate-prisma-client/action.yaml @@ -7,11 +7,11 @@ runs: id: cache-prisma uses: actions/cache@v3 with: - path: ./core/src/prisma.rs - key: prisma-${{ runner.os }}-${{ hashFiles('./core/prisma/*', './Cargo.toml') }} + path: ./core/src/prisma*.rs + key: prisma-0-${{ runner.os }}-${{ hashFiles('./core/prisma/*', './Cargo.toml') }} - name: Generate Prisma client working-directory: core if: steps.cache-prisma.outputs.cache-hit != 'true' shell: bash - run: cargo run -p prisma-cli --release -- generate + run: cargo run -p prisma-cli --bin prisma --release -- generate diff --git a/.gitignore b/.gitignore index 41d99ca63..907c91f7b 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ yalc.lock todos.md examples/*/*.lock /target -/core/src/prisma.rs +prisma*.rs /sdserver_data .spacedrive diff --git a/Cargo.lock b/Cargo.lock index f3e8718de..4a6b995f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,7 +45,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", ] @@ -412,7 +412,7 @@ checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide 0.5.4", "object", @@ -450,9 +450,9 @@ checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" [[package]] name = "bigdecimal" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1e50562e37200edf7c6c43e54a08e64a5553bfb59d9c297d5572512aa517256" +checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" dependencies = [ "num-bigint", "num-integer", @@ -510,15 +510,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitmaps" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -dependencies = [ - "typenum", -] - [[package]] name = "blake2" version = "0.10.4" @@ -537,7 +528,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if 1.0.0", + "cfg-if", "constant_time_eq", "digest 0.10.5", ] @@ -610,6 +601,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "builtin-psl-connectors" +version = "0.1.0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +dependencies = [ + "connection-string", + "either", + "enumflags2", + "indoc", + "lsp-types", + "once_cell", + "psl-core", + "regex", +] + [[package]] name = "bumpalo" version = "3.11.0" @@ -733,12 +739,6 @@ dependencies = [ "smallvec 1.10.0", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -751,7 +751,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", ] @@ -1030,6 +1030,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.16.1" @@ -1097,7 +1106,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1142,7 +1151,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -1152,7 +1161,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -1164,7 +1173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg 1.1.0", - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", @@ -1176,7 +1185,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -1186,7 +1195,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1280,13 +1289,13 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cuid" -version = "0.1.0" -source = "git+https://github.com/prisma/cuid-rust?rev=4ffb2e47c772af62fed3ddc92bb7fc444d19e159#4ffb2e47c772af62fed3ddc92bb7fc444d19e159" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a2891c056384f8461606f2579208164d67475fb17a0f442ac8d5981d3c2807" dependencies = [ - "hostname 0.1.5", + "hostname", "lazy_static", - "parking_lot 0.10.2", - "rand 0.7.3", + "rand 0.8.5", ] [[package]] @@ -1387,47 +1396,22 @@ version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "hashbrown 0.12.3", - "lock_api 0.4.9", + "lock_api", "once_cell", "parking_lot_core 0.9.3", ] [[package]] -name = "datamodel" +name = "datamodel-renderer" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ - "bigdecimal", - "chrono", - "datamodel-connector", - "diagnostics", - "dml", - "either", - "enumflags2", - "indoc", - "itertools", + "base64 0.13.1", "once_cell", - "parser-database", + "psl", "regex", - "schema-ast", - "serde", - "serde_json", - "sql-datamodel-connector", -] - -[[package]] -name = "datamodel-connector" -version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" -dependencies = [ - "diagnostics", - "enumflags2", - "lsp-types", - "parser-database", - "serde_json", - "url", ] [[package]] @@ -1467,9 +1451,10 @@ dependencies = [ [[package]] name = "diagnostics" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "colored", + "indoc", "pest", ] @@ -1508,7 +1493,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "dirs-sys-next", ] @@ -1543,40 +1528,36 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "dml" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "chrono", "cuid", + "either", "enumflags2", "indoc", - "native-types", "prisma-value", + "psl-core", + "schema-ast", "serde", "serde_json", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] name = "dmmf" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "bigdecimal", - "datamodel", "indexmap", "prisma-models", + "psl", "schema", "schema-builder", "serde", "serde_json", ] -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - [[package]] name = "dtoa" version = "0.4.8" @@ -1610,7 +1591,7 @@ version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1666,22 +1647,6 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" -[[package]] -name = "example-2" -version = "0.1.0" -dependencies = [ - "axum", - "dotenv", - "http", - "rspc", - "sd-sync", - "serde", - "serde_json", - "tokio", - "tower-http", - "uuid 1.2.1", -] - [[package]] name = "exr" version = "1.5.2" @@ -1759,9 +1724,9 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "windows-sys 0.36.1", ] @@ -1848,9 +1813,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -1863,9 +1828,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -1873,15 +1838,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -1890,9 +1855,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-lite" @@ -1911,9 +1876,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", @@ -1922,15 +1887,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-timer" @@ -1940,9 +1905,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -2066,7 +2031,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -2077,7 +2042,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2219,7 +2184,7 @@ dependencies = [ [[package]] name = "graphql-parser" version = "0.3.0" -source = "git+https://github.com/prisma/graphql-parser?rev=6a3f58bd879065588e710cb02b5bd30c1ce182c3#6a3f58bd879065588e710cb02b5bd30c1ce182c3" +source = "git+https://github.com/prisma/graphql-parser#6a3f58bd879065588e710cb02b5bd30c1ce182c3" dependencies = [ "combine 3.8.1", "indexmap", @@ -2399,16 +2364,6 @@ dependencies = [ "digest 0.10.5", ] -[[package]] -name = "hostname" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" -dependencies = [ - "libc", - "winutil", -] - [[package]] name = "hostname" version = "0.3.1" @@ -2638,20 +2593,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "im" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" -dependencies = [ - "bitmaps", - "rand_core 0.6.4", - "rand_xoshiro", - "sized-chunks", - "typenum", - "version_check", -] - [[package]] name = "image" version = "0.24.4" @@ -2761,7 +2702,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -2785,6 +2726,21 @@ dependencies = [ "syn", ] +[[package]] +name = "introspection-connector" +version = "0.1.0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +dependencies = [ + "anyhow", + "async-trait", + "enumflags2", + "psl", + "serde", + "serde_json", + "thiserror", + "user-facing-errors", +] + [[package]] name = "io-lifetimes" version = "1.0.3" @@ -2918,7 +2874,7 @@ dependencies = [ [[package]] name = "json-rpc-api-build" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "backtrace", "heck 0.3.3", @@ -3012,7 +2968,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "winapi", ] @@ -3060,15 +3016,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - [[package]] name = "lock_api" version = "0.4.9" @@ -3085,7 +3032,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -3094,7 +3041,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "generator", "scoped-tls", "serde", @@ -3327,11 +3274,12 @@ dependencies = [ [[package]] name = "migration-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "chrono", - "datamodel", "enumflags2", + "introspection-connector", + "psl", "sha2 0.9.9", "tracing", "tracing-error", @@ -3341,7 +3289,7 @@ dependencies = [ [[package]] name = "migration-core" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "async-trait", "chrono", @@ -3406,7 +3354,7 @@ dependencies = [ [[package]] name = "mobc" version = "0.7.3" -source = "git+https://github.com/prisma/mobc?tag=1.0.5#d50fd5de25f80880b0f533bcb48cc65f2c4960b0" +source = "git+https://github.com/prisma/mobc?tag=1.0.6#80462c4870a2bf6aab49da15c88c021bae531da8" dependencies = [ "async-trait", "futures-channel", @@ -3448,15 +3396,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "native-types" -version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "ndk" version = "0.6.0" @@ -3573,7 +3512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", ] @@ -3585,7 +3524,7 @@ checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ "autocfg 1.1.0", "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", ] @@ -3675,9 +3614,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.3.3" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg 1.1.0", "num-integer", @@ -3815,9 +3754,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "oorandom" @@ -3848,7 +3787,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -3994,16 +3933,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" -[[package]] -name = "parking_lot" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.7.2", -] - [[package]] name = "parking_lot" version = "0.11.2" @@ -4011,7 +3940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", - "lock_api 0.4.9", + "lock_api", "parking_lot_core 0.8.5", ] @@ -4021,34 +3950,20 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "lock_api 0.4.9", + "lock_api", "parking_lot_core 0.9.3", ] -[[package]] -name = "parking_lot_core" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "redox_syscall 0.1.57", - "smallvec 1.10.0", - "winapi", -] - [[package]] name = "parking_lot_core" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec 1.10.0", "winapi", ] @@ -4059,9 +3974,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec 1.10.0", "windows-sys 0.36.1", ] @@ -4069,7 +3984,7 @@ dependencies = [ [[package]] name = "parser-database" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "diagnostics", "either", @@ -4091,9 +4006,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "pathdiff" @@ -4385,7 +4300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ "autocfg 1.1.0", - "cfg-if 1.0.0", + "cfg-if", "libc", "log", "wepoll-ffi", @@ -4409,7 +4324,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "opaque-debug", "universal-hash", @@ -4432,22 +4347,28 @@ name = "prisma-cli" version = "0.1.0" dependencies = [ "prisma-client-rust-cli", + "sd-sync-generator", ] [[package]] name = "prisma-client-rust" version = "0.6.3" -source = "git+https://github.com/Brendonovich/prisma-client-rust.git?tag=0.6.3#c81f22fb287a2801da5a5961ed1ec9e99f5bee34" +source = "git+https://github.com/Brendonovich/prisma-client-rust.git?rev=6581dc56abadddada4a076ede88acc7ce038042c#6581dc56abadddada4a076ede88acc7ce038042c" dependencies = [ "base64 0.13.1", "bigdecimal", "chrono", - "datamodel", + "convert_case 0.6.0", + "diagnostics", + "dml", "dmmf", + "futures", "include_dir", "indexmap", "migration-core", + "paste", "prisma-models", + "psl", "query-connector", "query-core", "rspc", @@ -4460,18 +4381,22 @@ dependencies = [ "tokio", "tracing", "user-facing-errors", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] name = "prisma-client-rust-cli" version = "0.6.3" -source = "git+https://github.com/Brendonovich/prisma-client-rust.git?tag=0.6.3#c81f22fb287a2801da5a5961ed1ec9e99f5bee34" +source = "git+https://github.com/Brendonovich/prisma-client-rust.git?rev=6581dc56abadddada4a076ede88acc7ce038042c#6581dc56abadddada4a076ede88acc7ce038042c" dependencies = [ - "datamodel", + "directories", + "flate2", + "http", "prisma-client-rust-sdk", "proc-macro2", "quote", + "regex", + "reqwest", "serde", "serde_json", "serde_path_to_error", @@ -4482,21 +4407,17 @@ dependencies = [ [[package]] name = "prisma-client-rust-sdk" version = "0.6.3" -source = "git+https://github.com/Brendonovich/prisma-client-rust.git?tag=0.6.3#c81f22fb287a2801da5a5961ed1ec9e99f5bee34" +source = "git+https://github.com/Brendonovich/prisma-client-rust.git?rev=6581dc56abadddada4a076ede88acc7ce038042c#6581dc56abadddada4a076ede88acc7ce038042c" dependencies = [ "convert_case 0.5.0", - "datamodel", - "directories", + "dml", "dmmf", - "flate2", - "http", "prisma-models", "proc-macro2", + "psl", "query-core", "quote", - "regex", "request-handlers", - "reqwest", "serde", "serde_json", "serde_path_to_error", @@ -4507,16 +4428,15 @@ dependencies = [ [[package]] name = "prisma-models" version = "0.0.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "bigdecimal", "chrono", - "datamodel", "itertools", "once_cell", "prisma-value", + "psl", "serde", - "serde_derive", "serde_json", "thiserror", ] @@ -4524,7 +4444,7 @@ dependencies = [ [[package]] name = "prisma-value" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "base64 0.12.3", "bigdecimal", @@ -4533,7 +4453,7 @@ dependencies = [ "regex", "serde", "serde_json", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] @@ -4589,15 +4509,39 @@ dependencies = [ [[package]] name = "psl" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ - "datamodel", + "builtin-psl-connectors", + "dml", + "psl-core", +] + +[[package]] +name = "psl-core" +version = "0.1.0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +dependencies = [ + "bigdecimal", + "chrono", + "diagnostics", + "enumflags2", + "indoc", + "itertools", + "lsp-types", + "once_cell", + "parser-database", + "prisma-value", + "regex", + "schema-ast", + "serde", + "serde_json", + "url", ] [[package]] name = "quaint" version = "0.2.0-alpha.13" -source = "git+https://github.com/prisma/quaint?rev=fb4fe90682b4fecb485fd0d6975dd15a3bc9616b#fb4fe90682b4fecb485fd0d6975dd15a3bc9616b" +source = "git+https://github.com/prisma/quaint?rev=6df49f14efe99696e577ffb9902c83b09bec8de2#6df49f14efe99696e577ffb9902c83b09bec8de2" dependencies = [ "async-trait", "base64 0.12.3", @@ -4619,7 +4563,7 @@ dependencies = [ "tracing", "tracing-core", "url", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] @@ -4641,7 +4585,7 @@ dependencies = [ [[package]] name = "query-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "anyhow", "async-trait", @@ -4655,13 +4599,13 @@ dependencies = [ "serde_json", "thiserror", "user-facing-errors", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] name = "query-core" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "async-trait", "base64 0.12.3", @@ -4670,17 +4614,12 @@ dependencies = [ "connection-string", "crossbeam-queue", "cuid", - "datamodel", - "datamodel-connector", + "enumflags2", "futures", - "im", "indexmap", "itertools", "lazy_static", "lru", - "metrics 0.18.1", - "metrics-exporter-prometheus", - "metrics-util 0.12.1", "once_cell", "opentelemetry", "parking_lot 0.12.1", @@ -4688,7 +4627,9 @@ dependencies = [ "pin-utils", "prisma-models", "prisma-value", + "psl", "query-connector", + "query-engine-metrics", "schema", "schema-builder", "serde", @@ -4702,7 +4643,24 @@ dependencies = [ "tracing-subscriber", "url", "user-facing-errors", - "uuid 0.8.2", + "uuid 1.2.1", +] + +[[package]] +name = "query-engine-metrics" +version = "0.1.0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +dependencies = [ + "metrics 0.18.1", + "metrics-exporter-prometheus", + "metrics-util 0.12.1", + "once_cell", + "parking_lot 0.12.1", + "serde", + "serde_json", + "tracing", + "tracing-futures", + "tracing-subscriber", ] [[package]] @@ -4986,15 +4944,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "raw-cpuid" version = "10.6.0" @@ -5058,12 +5007,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "redox_syscall" version = "0.2.16" @@ -5080,7 +5023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.7", - "redox_syscall 0.2.16", + "redox_syscall", "thiserror", ] @@ -5122,16 +5065,16 @@ dependencies = [ [[package]] name = "request-handlers" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "bigdecimal", "connection-string", - "datamodel", "dmmf", "futures", "graphql-parser", "indexmap", "itertools", + "psl", "query-core", "serde", "serde_json", @@ -5413,17 +5356,17 @@ dependencies = [ [[package]] name = "schema" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ - "datamodel-connector", "once_cell", "prisma-models", + "psl", ] [[package]] name = "schema-ast" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "diagnostics", "pest", @@ -5433,13 +5376,13 @@ dependencies = [ [[package]] name = "schema-builder" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ - "datamodel-connector", "itertools", "lazy_static", "once_cell", "prisma-models", + "psl", "schema", ] @@ -5492,7 +5435,7 @@ dependencies = [ "fs_extra", "futures", "globset", - "hostname 0.3.1", + "hostname", "image", "include_dir", "int-enum", @@ -5507,6 +5450,7 @@ dependencies = [ "sd-crypto", "sd-ffmpeg", "sd-file-ext", + "sd-sync", "serde", "serde_json", "specta 0.0.4", @@ -5518,6 +5462,7 @@ dependencies = [ "tracing-android", "tracing-subscriber", "tracing-test", + "uhlc", "uuid 1.2.1", "webp", ] @@ -5617,14 +5562,29 @@ dependencies = [ name = "sd-sync" version = "0.1.0" dependencies = [ + "prisma-client-rust", "rand 0.8.5", "rspc", "serde", + "serde-value", "serde_json", "uhlc", "uuid 1.2.1", ] +[[package]] +name = "sd-sync-generator" +version = "0.1.0" +dependencies = [ + "nom", + "once_cell", + "prisma-client-rust-sdk", + "proc-macro2", + "quote", + "serde", + "thiserror", +] + [[package]] name = "sd-tunnel-utils" version = "0.1.0" @@ -5712,9 +5672,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.145" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" dependencies = [ "serde_derive", ] @@ -5740,9 +5700,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" dependencies = [ "proc-macro2", "quote", @@ -5865,7 +5825,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.5", ] @@ -5876,7 +5836,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.5", ] @@ -5888,7 +5848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -5900,7 +5860,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.5", ] @@ -5945,16 +5905,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" -[[package]] -name = "sized-chunks" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" -dependencies = [ - "bitmaps", - "typenum", -] - [[package]] name = "sketches-ddsketch" version = "0.1.3" @@ -6112,106 +6062,111 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" dependencies = [ - "lock_api 0.4.9", -] - -[[package]] -name = "sql-datamodel-connector" -version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" -dependencies = [ - "connection-string", - "datamodel-connector", - "either", - "enumflags2", - "lsp-types", - "native-types", - "once_cell", - "regex", - "serde_json", + "lock_api", ] [[package]] name = "sql-ddl" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" + +[[package]] +name = "sql-introspection-connector" +version = "0.1.0" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" +dependencies = [ + "anyhow", + "async-trait", + "bigdecimal", + "datamodel-renderer", + "enumflags2", + "introspection-connector", + "once_cell", + "psl", + "quaint", + "regex", + "serde", + "serde_json", + "sql-schema-describer", + "thiserror", + "tracing", + "tracing-futures", + "user-facing-errors", +] [[package]] name = "sql-migration-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "chrono", "connection-string", - "datamodel", "either", "enumflags2", "indoc", "migration-connector", - "native-types", "once_cell", + "psl", "quaint", "regex", "serde_json", "sql-ddl", + "sql-introspection-connector", "sql-schema-describer", "tokio", "tracing", "tracing-futures", "url", "user-facing-errors", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] name = "sql-query-connector" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "anyhow", "async-trait", "bigdecimal", "chrono", "cuid", - "datamodel", "futures", "itertools", "once_cell", "opentelemetry", "prisma-models", "prisma-value", + "psl", "quaint", "query-connector", "rand 0.7.3", "serde", "serde_json", - "sql-datamodel-connector", "thiserror", "tokio", "tracing", "tracing-futures", "tracing-opentelemetry", "user-facing-errors", - "uuid 0.8.2", + "uuid 1.2.1", ] [[package]] name = "sql-schema-describer" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "async-trait", "bigdecimal", "enumflags2", "indexmap", "indoc", - "native-types", "once_cell", - "prisma-value", + "psl", "quaint", "regex", "serde", - "serde_json", "tracing", "tracing-error", "tracing-futures", @@ -6347,9 +6302,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.102" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ "proc-macro2", "quote", @@ -6368,7 +6323,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7890fff842b8db56f2033ebee8f6efe1921475c3830c115995552914fb967580" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "core-foundation-sys", "libc", "ntapi", @@ -6685,10 +6640,10 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "remove_dir_all", "winapi", ] @@ -6945,7 +6900,7 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -7232,7 +7187,7 @@ dependencies = [ [[package]] name = "user-facing-error-macros" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "proc-macro2", "quote", @@ -7242,7 +7197,7 @@ dependencies = [ [[package]] name = "user-facing-errors" version = "0.1.0" -source = "git+https://github.com/Brendonovich/prisma-engines?rev=06a1b97ff1ca597521ec9f3d10c1e274065f5e93#06a1b97ff1ca597521ec9f3d10c1e274065f5e93" +source = "git+https://github.com/Brendonovich/prisma-engines?rev=43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0#43fcfd1b5c0be59e414fb03cf7b7712e1661b6d0" dependencies = [ "backtrace", "indoc", @@ -7264,10 +7219,6 @@ name = "uuid" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom 0.2.7", - "serde", -] [[package]] name = "uuid" @@ -7366,7 +7317,7 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -7391,7 +7342,7 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -7922,15 +7873,6 @@ dependencies = [ "toml", ] -[[package]] -name = "winutil" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" -dependencies = [ - "winapi", -] - [[package]] name = "wry" version = "0.21.1" diff --git a/Cargo.toml b/Cargo.toml index 1e7da99a3..87874b2d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "crates/*", # "crates/p2p/tunnel", # "crates/p2p/tunnel/utils", - "crates/sync/example/api", "apps/cli", "apps/desktop/src-tauri", "apps/mobile/rust", @@ -13,18 +12,21 @@ members = [ ] [workspace.dependencies] -prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust.git", tag = "0.6.3", features = [ +prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust.git", rev = "6581dc56abadddada4a076ede88acc7ce038042c", features = [ "rspc", "sqlite-create-many", "migrations", "sqlite", ], default-features = false } -prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust.git", tag = "0.6.3", features = [ +prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust.git", rev = "6581dc56abadddada4a076ede88acc7ce038042c", features = [ "rspc", "sqlite-create-many", "migrations", "sqlite", ], default-features = false } +prisma-client-rust-sdk = { git = "https://github.com/Brendonovich/prisma-client-rust.git", rev = "6581dc56abadddada4a076ede88acc7ce038042c", features = [ + "sqlite", +], default-features = false } rspc = { version = "0.1.2" } normi = { version = "0.0.1" } diff --git a/core/Cargo.toml b/core/Cargo.toml index 94ce81be4..d6091f536 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -59,6 +59,7 @@ ffmpeg-next = { version = "5.1.1", optional = true, features = [] } sd-ffmpeg = { path = "../crates/ffmpeg", optional = true } sd-crypto = { path = "../crates/crypto", features = ["rspc", "serde"] } sd-file-ext = { path = "../crates/file-ext"} +sd-sync = { path = "../crates/sync" } fs_extra = "1.2.0" tracing = "0.1.36" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } @@ -70,6 +71,7 @@ globset = { version = "^0.4.9", features = ["serde1"] } itertools = "^0.10.5" enumflags2 = "0.7.5" notify = { version = "5.0.0", default-features = false, features = ["macos_kqueue"], optional = true } +uhlc = "0.5.1" [dev-dependencies] tempfile = "^3.3.0" diff --git a/core/prisma/migrations/20230105021821_owned_shared_operations/migration.sql b/core/prisma/migrations/20230105021821_owned_shared_operations/migration.sql new file mode 100644 index 000000000..717af66ce --- /dev/null +++ b/core/prisma/migrations/20230105021821_owned_shared_operations/migration.sql @@ -0,0 +1,53 @@ +-- CreateTable +CREATE TABLE "owned_operation" ( + "id" BLOB NOT NULL PRIMARY KEY, + "timestamp" BIGINT NOT NULL, + "data" BLOB NOT NULL, + "model" TEXT NOT NULL, + "node_id" INTEGER NOT NULL, + CONSTRAINT "owned_operation_node_id_fkey" FOREIGN KEY ("node_id") REFERENCES "node" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "shared_operation" ( + "id" BLOB NOT NULL PRIMARY KEY, + "timestamp" BIGINT NOT NULL, + "model" TEXT NOT NULL, + "record_id" BLOB NOT NULL, + "kind" TEXT NOT NULL, + "data" BLOB NOT NULL, + "node_id" INTEGER NOT NULL, + CONSTRAINT "shared_operation_node_id_fkey" FOREIGN KEY ("node_id") REFERENCES "node" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_object" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "cas_id" TEXT NOT NULL, + "integrity_checksum" TEXT, + "name" TEXT, + "extension" TEXT, + "kind" INTEGER NOT NULL DEFAULT 0, + "size_in_bytes" TEXT NOT NULL DEFAULT '0', + "key_id" INTEGER, + "hidden" BOOLEAN NOT NULL DEFAULT false, + "favorite" BOOLEAN NOT NULL DEFAULT false, + "important" BOOLEAN NOT NULL DEFAULT false, + "has_thumbnail" BOOLEAN NOT NULL DEFAULT false, + "has_thumbstrip" BOOLEAN NOT NULL DEFAULT false, + "has_video_preview" BOOLEAN NOT NULL DEFAULT false, + "ipfs_id" TEXT, + "note" TEXT, + "date_created" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "date_modified" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "date_indexed" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "object_key_id_fkey" FOREIGN KEY ("key_id") REFERENCES "key" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_object" ("cas_id", "date_created", "date_indexed", "date_modified", "extension", "favorite", "has_thumbnail", "has_thumbstrip", "has_video_preview", "hidden", "id", "important", "integrity_checksum", "ipfs_id", "key_id", "kind", "name", "note", "size_in_bytes") SELECT "cas_id", "date_created", "date_indexed", "date_modified", "extension", "favorite", "has_thumbnail", "has_thumbstrip", "has_video_preview", "hidden", "id", "important", "integrity_checksum", "ipfs_id", "key_id", "kind", "name", "note", "size_in_bytes" FROM "object"; +DROP TABLE "object"; +ALTER TABLE "new_object" RENAME TO "object"; +CREATE UNIQUE INDEX "object_cas_id_key" ON "object"("cas_id"); +CREATE UNIQUE INDEX "object_integrity_checksum_key" ON "object"("integrity_checksum"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/core/prisma/schema.prisma b/core/prisma/schema.prisma index a3f86e9e4..89262ea04 100644 --- a/core/prisma/schema.prisma +++ b/core/prisma/schema.prisma @@ -8,6 +8,38 @@ generator client { output = "../src/prisma.rs" } +generator sync { + provider = "cargo prisma-sync" + output = "../src/prisma_sync.rs" +} + +model OwnedOperation { + id Bytes @id + timestamp BigInt + data Bytes + model String + + node_id Int + node Node @relation(fields: [node_id], references: [id]) + + @@map("owned_operation") +} + +model SharedOperation { + id Bytes @id + timestamp BigInt + model String + + record_id Bytes + kind String + data Bytes + + node_id Int + node Node @relation(fields: [node_id], references: [id]) + + @@map("shared_operation") +} + model SyncEvent { id Int @id @default(autoincrement()) node_id Int @@ -53,7 +85,9 @@ model Node { sync_events SyncEvent[] jobs Job[] - Location Location[] + Location Location[] + OwnedOperation OwnedOperation[] + SharedOperation SharedOperation[] @@map("node") } @@ -106,7 +140,7 @@ model Object { name String? extension String? kind Int @default(0) - size_in_bytes String + size_in_bytes String @default("0") key_id Int? // handy ways to mark an object hidden Boolean @default(false) diff --git a/core/src/lib.rs b/core/src/lib.rs index 052a44303..d7d532797 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -20,10 +20,12 @@ pub(crate) mod library; pub(crate) mod location; pub(crate) mod node; pub(crate) mod object; +pub(crate) mod sync; pub(crate) mod util; pub(crate) mod volume; pub(crate) mod prisma; +pub(crate) mod prisma_sync; #[derive(Clone)] pub struct NodeContext { diff --git a/core/src/library/library_ctx.rs b/core/src/library/library_ctx.rs index 2b2f667f2..246e7654c 100644 --- a/core/src/library/library_ctx.rs +++ b/core/src/library/library_ctx.rs @@ -1,6 +1,6 @@ use crate::{ api::CoreEvent, job::DynJob, location::LocationManager, node::NodeConfigManager, - prisma::PrismaClient, NodeContext, + prisma::PrismaClient, sync::SyncManager, NodeContext, }; use std::{ @@ -25,6 +25,7 @@ pub struct LibraryContext { pub config: LibraryConfig, /// db holds the database client for the current library. pub db: Arc, + pub sync: Arc, /// key manager that provides encryption keys to functions that require them pub key_manager: Arc, /// node_local_id holds the local ID of the node which is running the library. diff --git a/core/src/library/library_manager.rs b/core/src/library/library_manager.rs index 333976d3b..ff9546253 100644 --- a/core/src/library/library_manager.rs +++ b/core/src/library/library_manager.rs @@ -2,6 +2,7 @@ use crate::{ invalidate_query, node::Platform, prisma::{node, PrismaClient}, + sync::SyncManager, util::{ db::{load_and_migrate, write_storedkey_to_db}, seeder::{indexer_rules_seeder, SeederError}, @@ -342,11 +343,14 @@ impl LibraryManager { .exec() .await?; + let (sync_manager, _) = SyncManager::new(db.clone(), id); + Ok(LibraryContext { id, local_id: node_data.id, config, key_manager: create_keymanager(&db).await?, + sync: Arc::new(sync_manager), db, node_local_id: node_data.id, node_context, diff --git a/core/src/location/file_path_helper.rs b/core/src/location/file_path_helper.rs index a70a8217f..cbdd2dd9a 100644 --- a/core/src/location/file_path_helper.rs +++ b/core/src/location/file_path_helper.rs @@ -2,7 +2,6 @@ use crate::{library::LibraryContext, prisma::file_path}; use std::sync::atomic::{AtomicI32, Ordering}; -use chrono::{DateTime, Utc}; use prisma_client_rust::{Direction, QueryError}; static LAST_FILE_PATH_ID: AtomicI32 = AtomicI32::new(0); @@ -81,61 +80,3 @@ pub async fn create_file_path( Ok(created_path) } - -pub struct FilePathBatchCreateEntry { - pub id: i32, - pub location_id: i32, - pub materialized_path: String, - pub name: String, - pub extension: Option, - pub parent_id: Option, - pub is_dir: bool, - pub created_at: DateTime, -} - -pub async fn create_many_file_paths( - library_ctx: &LibraryContext, - entries: Vec, -) -> Result { - library_ctx - .db - .file_path() - .create_many( - entries - .into_iter() - .map( - |FilePathBatchCreateEntry { - id, - location_id, - mut materialized_path, - name, - extension, - parent_id, - is_dir, - created_at, - }| { - // If this new file_path is a directory, materialized_path must end with "/" - if is_dir && !materialized_path.ends_with('/') { - materialized_path += "/"; - } - - file_path::create_unchecked( - id, - location_id, - materialized_path, - name, - vec![ - file_path::is_dir::set(is_dir), - file_path::parent_id::set(parent_id), - file_path::extension::set(extension), - file_path::date_created::set(created_at.into()), - ], - ) - }, - ) - .collect(), - ) - .skip_duplicates() - .exec() - .await -} diff --git a/core/src/location/indexer/indexer_job.rs b/core/src/location/indexer/indexer_job.rs index 1970f71ea..bca20c8b1 100644 --- a/core/src/location/indexer/indexer_job.rs +++ b/core/src/location/indexer/indexer_job.rs @@ -15,14 +15,12 @@ use std::{ use chrono::{DateTime, Utc}; use itertools::Itertools; use serde::{Deserialize, Serialize}; +use serde_json::json; use tokio::time::Instant; use tracing::info; use super::{ - super::file_path_helper::{ - create_many_file_paths, get_max_file_path_id, set_max_file_path_id, - FilePathBatchCreateEntry, - }, + super::file_path_helper::{get_max_file_path_id, set_max_file_path_id}, rules::IndexerRule, walk::{walk, WalkEntry}, }; @@ -233,11 +231,12 @@ impl StatefulJob for IndexerJob { .data .as_ref() .expect("critical error: missing data on job state"); + let db = &ctx.library_ctx.db; let location_path = &data.location_path; let location_id = state.init.location.id; - let entries = state.steps[0] + let (sync_stuff, paths): (Vec<_>, Vec<_>) = state.steps[0] .iter() .map(|entry| { let name; @@ -253,7 +252,7 @@ impl StatefulJob for IndexerJob { extension = Some(extract_name(entry.path.extension()).to_lowercase()); name = extract_name(entry.path.file_stem()); } - let materialized_path = entry + let mut materialized_path = entry .path .strip_prefix(location_path) .unwrap() @@ -261,20 +260,54 @@ impl StatefulJob for IndexerJob { .expect("Found non-UTF-8 path") .to_string(); - FilePathBatchCreateEntry { - id: entry.file_id, - location_id, - materialized_path, - name, - extension, - parent_id: entry.parent_id, - is_dir: entry.is_dir, - created_at: entry.created_at, + if entry.is_dir && !materialized_path.ends_with('/') { + materialized_path += "/"; } - }) - .collect(); - let count = create_many_file_paths(&ctx.library_ctx, entries).await?; + use file_path::*; + + ( + ( + json!({ + "id": entry.file_id, + "location_id": state.init.location.pub_id, + }), + [ + ("materialized_path", json!(materialized_path.clone())), + ("name", json!(name.clone())), + ("is_dir", json!(entry.is_dir)), + ("extension", json!(extension.clone())), + ("parent_id", json!(entry.parent_id)), + ("date_created", json!(entry.created_at)), + ], + ), + file_path::create_unchecked( + entry.file_id, + location_id, + materialized_path, + name, + vec![ + is_dir::set(entry.is_dir), + extension::set(extension), + parent_id::set(entry.parent_id), + date_created::set(entry.created_at.into()), + ], + ), + ) + }) + .unzip(); + + let count = ctx + .library_ctx + .sync + .write_op( + db, + ctx.library_ctx + .sync + .owned_create_many("FilePath", sync_stuff, true), + db.file_path().create_many(paths).skip_duplicates(), + ) + .await?; info!("Inserted {count} records"); diff --git a/core/src/location/manager/watcher/utils.rs b/core/src/location/manager/watcher/utils.rs index bd31375a2..415a5b372 100644 --- a/core/src/location/manager/watcher/utils.rs +++ b/core/src/location/manager/watcher/utils.rs @@ -171,12 +171,12 @@ async fn inner_create_file( .object() .upsert( object::cas_id::equals(cas_id.clone()), - ( + object::create_unchecked( cas_id.clone(), - size_str.clone(), vec![ object::date_created::set(date_created), object::kind::set(kind.int_value()), + object::size_in_bytes::set(size_str.clone()), ], ), vec![ diff --git a/core/src/location/mod.rs b/core/src/location/mod.rs index 892daf7c4..e4de4646a 100644 --- a/core/src/location/mod.rs +++ b/core/src/location/mod.rs @@ -9,14 +9,15 @@ use crate::{ prisma::{file_path, indexer_rules_in_location, location, node, object}, }; +use rspc::Type; +use serde::Deserialize; +use serde_json::json; use std::{ collections::HashSet, path::{Path, PathBuf}, }; use prisma_client_rust::QueryError; -use rspc::Type; -use serde::Deserialize; use tokio::{fs, io}; use tracing::{debug, info}; use uuid::Uuid; @@ -317,6 +318,8 @@ async fn create_location( location_path: impl AsRef, indexer_rules_ids: &[i32], ) -> Result { + let db = &ctx.db; + let location_name = location_path .as_ref() .file_name() @@ -325,26 +328,38 @@ async fn create_location( .unwrap() .to_string(); - let mut location = ctx - .db - .location() - .create( - location_pub_id.as_bytes().to_vec(), - node::id::equals(ctx.node_local_id), - vec![ - location::name::set(Some(location_name.clone())), - location::is_online::set(true), - location::local_path::set(Some( - location_path - .as_ref() - .to_str() - .expect("Found non-UTF-8 path") - .to_string(), - )), - ], + let local_path = location_path + .as_ref() + .to_str() + .expect("Found non-UTF-8 path") + .to_string(); + + let location = ctx + .sync + .write_op( + db, + ctx.sync.owned_create( + "Location", + json!({ "id": location_pub_id.as_bytes() }), + [ + ("node", json!({ "pub_id": ctx.id.as_bytes() })), + ("name", json!(location_name)), + ("is_online", json!(true)), + ("local_path", json!(&local_path)), + ], + ), + db.location() + .create( + location_pub_id.as_bytes().to_vec(), + node::id::equals(ctx.node_local_id), + vec![ + location::name::set(Some(location_name.clone())), + location::is_online::set(true), + location::local_path::set(Some(local_path)), + ], + ) + .include(indexer_job_location::include()), ) - .include(indexer_job_location::include()) - .exec() .await?; if !indexer_rules_ids.is_empty() { @@ -352,7 +367,7 @@ async fn create_location( } // Updating our location variable to include information about the indexer rules - location = fetch_location(ctx, location.id) + let location = fetch_location(ctx, location.id) .include(indexer_job_location::include()) .exec() .await? diff --git a/core/src/object/identifier_job/full_identifier_job.rs b/core/src/object/identifier_job/full_identifier_job.rs index c4f896a9c..0f798aa05 100644 --- a/core/src/object/identifier_job/full_identifier_job.rs +++ b/core/src/object/identifier_job/full_identifier_job.rs @@ -69,10 +69,9 @@ impl StatefulJob for FullFileIdentifierJob { info!("Identifying orphan File Paths..."); let location_id = state.init.location_id; + let db = &ctx.library_ctx.db; - let location = ctx - .library_ctx - .db + let location = db .location() .find_unique(location::id::equals(location_id)) .exec() @@ -97,9 +96,7 @@ impl StatefulJob for FullFileIdentifierJob { // update job with total task count based on orphan file_paths count ctx.progress(vec![JobReportUpdate::TaskCount(task_count)]); - let first_path_id = ctx - .library_ctx - .db + let first_path_id = db .file_path() .find_first(orphan_path_filters(location_id, None)) .exec() diff --git a/core/src/object/identifier_job/mod.rs b/core/src/object/identifier_job/mod.rs index a4ec5bb53..c2634b39a 100644 --- a/core/src/object/identifier_job/mod.rs +++ b/core/src/object/identifier_job/mod.rs @@ -5,6 +5,7 @@ use crate::{ prisma::{file_path, object}, }; use chrono::{DateTime, FixedOffset}; +use serde_json::json; use std::{ collections::{HashMap, HashSet}, path::{Path, PathBuf}, @@ -90,36 +91,49 @@ async fn batch_update_file_paths( objects: &[object::Data], cas_id_lookup: &HashMap>, ) -> Result, QueryError> { - let mut file_path_updates = Vec::new(); + let (sync, updates): (Vec<_>, Vec<_>) = objects + .iter() + .flat_map(|object| { + let file_path_ids = cas_id_lookup.get(&object.cas_id).unwrap(); + let sync = &library.sync; - objects.iter().for_each(|object| { - let file_path_ids = cas_id_lookup.get(&object.cas_id).unwrap(); + file_path_ids.iter().map(|file_path_id| { + info!( + "Linking: ", + file_path_id, object.id + ); - file_path_updates.extend(file_path_ids.iter().map(|file_path_id| { - info!( - "Linking: ", - file_path_id, object.id - ); - library.db.file_path().update( - file_path::location_id_id(location_id, *file_path_id), - vec![file_path::object_id::set(Some(object.id))], - ) - })); - }); + ( + sync.owned_update( + "FilePath", + json!({ + "id": file_path_id, + "location_id": location_id + }), + [("object", json!({ "cas_id": object.cas_id }))], + ), + library.db.file_path().update( + file_path::location_id_id(location_id, *file_path_id), + vec![file_path::object::connect(object::id::equals(object.id))], + ), + ) + }) + }) + .unzip(); info!( "Updating {} file paths for {} objects", - file_path_updates.len(), + updates.len(), objects.len() ); - library.db._batch(file_path_updates).await + library.sync.write_ops(&library.db, sync, updates).await } async fn generate_provisional_objects( location_path: impl AsRef, file_paths: &[file_path::Data], -) -> HashMap)> { +) -> HashMap)> { let mut provisional_objects = HashMap::with_capacity(file_paths.len()); // analyze each file_path @@ -145,10 +159,10 @@ async fn generate_provisional_objects( file_path_id, object::create_unchecked( cas_id, - size_str, vec![ object::date_created::set(date_created), object::kind::set(kind.int_value()), + object::size_in_bytes::set(size_str), ], ), ); @@ -159,6 +173,7 @@ async fn generate_provisional_objects( } }; } + provisional_objects } @@ -175,7 +190,7 @@ async fn identifier_job_step( let unique_cas_ids = provisional_objects .values() - .map(|(cas_id, _, _)| cas_id.clone()) + .map(|(cas_id, _)| cas_id.clone()) .collect::>() .into_iter() .collect::>(); @@ -184,7 +199,7 @@ async fn identifier_job_step( let mut cas_id_lookup: HashMap> = HashMap::with_capacity(unique_cas_ids.len()); // populate cas_id_lookup with file_path_ids - for (file_path_id, (cas_id, _, _)) in provisional_objects.iter() { + for (file_path_id, (cas_id, _)) in provisional_objects.iter() { cas_id_lookup .entry(cas_id.clone()) .or_insert_with(Vec::new) @@ -223,12 +238,12 @@ async fn identifier_job_step( // extract objects that don't already exist in the database let new_objects = provisional_objects .into_iter() - .filter(|(_, (cas_id, _, _))| !existing_object_cas_ids.contains(cas_id)) + .filter(|(_, (cas_id, _))| !existing_object_cas_ids.contains(cas_id)) .collect::>(); let new_objects_cas_ids = new_objects .iter() - .map(|(_, (cas_id, _, _))| cas_id.clone()) + .map(|(_, (cas_id, _))| cas_id.clone()) .collect::>(); info!( @@ -243,12 +258,7 @@ async fn identifier_job_step( let total_created_files = library .db .object() - .create_many( - new_objects - .into_iter() - .map(|(_, (cas_id, size, params))| (cas_id, size, params)) - .collect(), - ) + .create_many(new_objects.into_iter().map(|(_, p)| p).collect()) .skip_duplicates() .exec() .await diff --git a/core/src/sync/mod.rs b/core/src/sync/mod.rs new file mode 100644 index 000000000..29ed65bff --- /dev/null +++ b/core/src/sync/mod.rs @@ -0,0 +1,519 @@ +use futures::future::join_all; +use sd_sync::*; +use serde::Deserialize; +use serde_json::{from_slice, from_value, to_vec, Value}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; +use tokio::sync::mpsc::{self, Receiver, Sender}; +use uhlc::{HLCBuilder, HLC, NTP64}; +use uuid::Uuid; + +use crate::prisma::{ + file_path, location, node, object, owned_operation, shared_operation, PrismaClient, +}; + +pub struct SyncManager { + db: Arc, + node: Uuid, + _clocks: HashMap, + clock: HLC, + tx: Sender, +} + +impl SyncManager { + pub fn new(db: Arc, node: Uuid) -> (Self, Receiver) { + let (tx, rx) = mpsc::channel(64); + + ( + Self { + db, + node, + clock: HLCBuilder::new().with_id(node.into()).build(), + _clocks: Default::default(), + tx, + }, + rx, + ) + } + + pub async fn write_ops<'item, Q: prisma_client_rust::BatchItem<'item>>( + &self, + tx: &PrismaClient, + ops: Vec, + queries: Q, + ) -> prisma_client_rust::Result<::ReturnValue> { + let owned = ops + .iter() + .filter_map(|op| match &op.typ { + CRDTOperationType::Owned(owned_op) => Some(tx.owned_operation().create( + op.id.as_bytes().to_vec(), + op.timestamp.0 as i64, + to_vec(&owned_op.items).unwrap(), + owned_op.model.clone(), + node::pub_id::equals(op.node.as_bytes().to_vec()), + vec![], + )), + _ => None, + }) + .collect::>(); + + let shared = ops + .iter() + .filter_map(|op| match &op.typ { + CRDTOperationType::Shared(shared_op) => { + let kind = match &shared_op.data { + SharedOperationData::Create(_) => "c", + SharedOperationData::Update { .. } => "u", + SharedOperationData::Delete => "d", + }; + + Some(tx.shared_operation().create( + op.id.as_bytes().to_vec(), + op.timestamp.0 as i64, + shared_op.model.to_string(), + to_vec(&shared_op.record_id).unwrap(), + kind.to_string(), + to_vec(&shared_op.data).unwrap(), + node::pub_id::equals(op.node.as_bytes().to_vec()), + vec![], + )) + } + _ => None, + }) + .collect::>(); + + let (res, _) = tx._batch((queries, (owned, shared))).await?; + + for op in ops { + self.tx.send(op).await.ok(); + } + + Ok(res) + } + + pub async fn write_op<'query, Q: prisma_client_rust::Query<'query>>( + &self, + tx: &PrismaClient, + op: CRDTOperation, + query: Q, + ) -> prisma_client_rust::Result { + let ret = match &op.typ { + CRDTOperationType::Owned(owned_op) => { + tx._batch(( + tx.owned_operation().create( + op.id.as_bytes().to_vec(), + op.timestamp.0 as i64, + to_vec(&owned_op.items).unwrap(), + owned_op.model.clone(), + node::pub_id::equals(op.node.as_bytes().to_vec()), + vec![], + ), + query, + )) + .await? + .1 + } + CRDTOperationType::Shared(shared_op) => { + let kind = match &shared_op.data { + SharedOperationData::Create(_) => "c", + SharedOperationData::Update { .. } => "u", + SharedOperationData::Delete => "d", + }; + + tx._batch(( + tx.shared_operation().create( + op.id.as_bytes().to_vec(), + op.timestamp.0 as i64, + shared_op.model.to_string(), + to_vec(&shared_op.record_id).unwrap(), + kind.to_string(), + to_vec(&shared_op.data).unwrap(), + node::pub_id::equals(op.node.as_bytes().to_vec()), + vec![], + ), + query, + )) + .await? + .1 + } + _ => todo!(), + }; + + self.tx.send(op).await.ok(); + + Ok(ret) + } + + pub async fn get_ops(&self) -> prisma_client_rust::Result> { + owned_operation::include!(owned_op_with_node { node }); + + impl TryInto for owned_op_with_node::Data { + type Error = (); + + fn try_into(self) -> Result { + let id = Uuid::from_slice(&self.id).map_err(|_| ())?; + let node = Uuid::from_slice(&self.node.pub_id).map_err(|_| ())?; + + Ok(CRDTOperation { + id, + node, + timestamp: NTP64(self.timestamp as u64), + typ: CRDTOperationType::Owned(OwnedOperation { + model: self.model, + items: serde_json::from_slice(&self.data).unwrap(), + }), + }) + } + } + + shared_operation::include!(shared_op_with_node { node }); + + impl TryInto for shared_op_with_node::Data { + type Error = (); + + fn try_into(self) -> Result { + let id = Uuid::from_slice(&self.id).map_err(|_| ())?; + let node = Uuid::from_slice(&self.node.pub_id).map_err(|_| ())?; + + Ok(CRDTOperation { + id, + node, + timestamp: NTP64(self.timestamp as u64), + typ: CRDTOperationType::Shared(SharedOperation { + record_id: serde_json::from_slice(&self.record_id).unwrap(), + model: self.model, + data: from_slice(&self.data).unwrap(), + }), + }) + } + } + + let owned = self + .db + .owned_operation() + .find_many(vec![]) + .include(owned_op_with_node::include()) + .exec() + .await? + .into_iter() + .map(TryInto::try_into); + let shared = self + .db + .shared_operation() + .find_many(vec![]) + .include(shared_op_with_node::include()) + .exec() + .await? + .into_iter() + .map(TryInto::try_into); + + let mut result: Vec = owned.chain(shared).flatten().collect(); + + result.sort_by(|a, b| a.timestamp.cmp(&b.timestamp)); + + Ok(result) + } + + pub async fn ingest_op(&self, op: CRDTOperation) -> prisma_client_rust::Result<()> { + match op.typ { + CRDTOperationType::Owned(owned_op) => match owned_op.model.as_str() { + "FilePath" => { + #[derive(Deserialize)] + struct FilePathId { + location_id: Vec, + id: i32, + } + + for item in owned_op.items { + let id: FilePathId = serde_json::from_value(item.id).unwrap(); + + let location = self + .db + .location() + .find_unique(location::pub_id::equals(id.location_id)) + .select(location::select!({ id })) + .exec() + .await? + .unwrap(); + + match item.data { + OwnedOperationData::Create(mut data) => { + self.db + .file_path() + .create( + id.id, + location::id::equals(location.id), + serde_json::from_value( + data.remove("materialized_path").unwrap(), + ) + .unwrap(), + serde_json::from_value(data.remove("name").unwrap()) + .unwrap(), + data.into_iter() + .flat_map(|(k, v)| { + file_path::SetParam::deserialize(&k, v) + }) + .collect(), + ) + .exec() + .await?; + } + OwnedOperationData::CreateMany { + values, + skip_duplicates, + } => { + let location_ids = values + .iter() + .map(|(id, _)| { + serde_json::from_value::(id.clone()) + .unwrap() + .location_id + }) + .collect::>(); + let location_id_mappings = + join_all(location_ids.iter().map(|id| async move { + self.db + .location() + .find_unique(location::pub_id::equals(id.clone())) + .exec() + .await + .map(|o| o.map(|v| (id, v.id))) + })) + .await + .into_iter() + .flatten() + .flatten() + .collect::>(); + + let mut q = self.db.file_path().create_many( + values + .into_iter() + .map(|(id, mut data)| { + let id: FilePathId = + serde_json::from_value(id).unwrap(); + + file_path::create_unchecked( + id.id, + *location_id_mappings.get(&id.location_id).unwrap(), + serde_json::from_value( + data.remove("materialized_path").unwrap(), + ) + .unwrap(), + serde_json::from_value( + data.remove("name").unwrap(), + ) + .unwrap(), + data.into_iter() + .flat_map(|(k, v)| { + file_path::SetParam::deserialize(&k, v) + }) + .collect(), + ) + }) + .collect(), + ); + + if skip_duplicates { + q = q.skip_duplicates() + } + + q.exec().await?; + } + OwnedOperationData::Update(data) => { + self.db + .file_path() + .update( + file_path::location_id_id(location.id, id.id), + data.into_iter() + .flat_map(|(k, v)| { + file_path::SetParam::deserialize(&k, v) + }) + .collect(), + ) + .exec() + .await?; + } + _ => todo!(), + } + } + } + "Location" => { + #[derive(Deserialize)] + struct LocationId { + id: Vec, + } + + for item in owned_op.items { + let id: LocationId = serde_json::from_value(item.id).unwrap(); + + match item.data { + OwnedOperationData::Create(mut data) => { + self.db + .location() + .create( + id.id, + { + let val: std::collections::HashMap = + from_value(data.remove("node").unwrap()).unwrap(); + let val = val.into_iter().next().unwrap(); + + node::UniqueWhereParam::deserialize(&val.0, val.1) + .unwrap() + }, + data.into_iter() + .flat_map(|(k, v)| { + location::SetParam::deserialize(&k, v) + }) + .collect(), + ) + .exec() + .await?; + } + _ => todo!(), + } + } + } + _ => {} + }, + CRDTOperationType::Shared(shared_op) => match shared_op.model.as_str() { + "Object" => { + let cas_id: String = from_value(shared_op.record_id).unwrap(); + + match shared_op.data { + SharedOperationData::Create(_) => { + self.db + .object() + .upsert( + object::cas_id::equals(cas_id.clone()), + (cas_id, vec![]), + vec![], + ) + .exec() + .await + .ok(); + } + SharedOperationData::Update { field, value } => { + self.db + .object() + .update( + object::cas_id::equals(cas_id), + vec![object::SetParam::deserialize(&field, value).unwrap()], + ) + .exec() + .await?; + } + _ => todo!(), + } + } + _ => todo!(), + }, + _ => {} + } + + Ok(()) + } + + fn new_op(&self, typ: CRDTOperationType) -> CRDTOperation { + let timestamp = self.clock.new_timestamp(); + + CRDTOperation { + node: self.node, + timestamp: *timestamp.get_time(), + id: Uuid::new_v4(), + typ, + } + } + + pub fn owned_create( + &self, + model: &str, + id: Value, + values: [(&'static str, Value); SIZE], + ) -> CRDTOperation { + self.new_op(CRDTOperationType::Owned(OwnedOperation { + model: model.to_string(), + items: [(id, values)] + .into_iter() + .map(|(id, data)| OwnedOperationItem { + id, + data: OwnedOperationData::Create( + data.into_iter().map(|(k, v)| (k.to_string(), v)).collect(), + ), + }) + .collect(), + })) + } + + pub fn owned_create_many( + &self, + model: &str, + data: impl IntoIterator, + skip_duplicates: bool, + ) -> CRDTOperation { + self.new_op(CRDTOperationType::Owned(OwnedOperation { + model: model.to_string(), + items: vec![OwnedOperationItem { + id: Value::Null, + data: OwnedOperationData::CreateMany { + values: data + .into_iter() + .map(|(id, data)| { + ( + id, + data.into_iter().map(|(k, v)| (k.to_string(), v)).collect(), + ) + }) + .collect(), + skip_duplicates, + }, + }], + })) + } + + pub fn owned_update( + &self, + model: &str, + id: Value, + values: [(&'static str, Value); SIZE], + ) -> CRDTOperation { + self.new_op(CRDTOperationType::Owned(OwnedOperation { + model: model.to_string(), + items: [(id, values)] + .into_iter() + .map(|(id, data)| OwnedOperationItem { + id, + data: OwnedOperationData::Update( + data.into_iter().map(|(k, v)| (k.to_string(), v)).collect(), + ), + }) + .collect(), + })) + } + + pub fn shared_create(&self, model: &str, record_id: Value) -> CRDTOperation { + self.new_op(CRDTOperationType::Shared(SharedOperation { + model: model.to_string(), + record_id, + data: SharedOperationData::Create(SharedOperationCreateData::Atomic), + })) + } + + pub fn shared_update( + &self, + model: &str, + record_id: Value, + field: &str, + value: Value, + ) -> CRDTOperation { + self.new_op(CRDTOperationType::Shared(SharedOperation { + model: model.to_string(), + record_id, + data: SharedOperationData::Update { + field: field.to_string(), + value, + }, + })) + } +} diff --git a/crates/crypto/src/header/serialization.rs b/crates/crypto/src/header/serialization.rs index 46a27851b..205e72455 100644 --- a/crates/crypto/src/header/serialization.rs +++ b/crates/crypto/src/header/serialization.rs @@ -143,8 +143,8 @@ impl HashingAlgorithm { impl Display for HashingAlgorithm { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { - Self::Argon2id(p) => write!(f, "Argon2id ({})", p), - Self::BalloonBlake3(p) => write!(f, "BLAKE3-Balloon ({})", p), + Self::Argon2id(p) => write!(f, "Argon2id ({p})"), + Self::BalloonBlake3(p) => write!(f, "BLAKE3-Balloon ({p})"), } } } diff --git a/crates/prisma-cli/Cargo.toml b/crates/prisma-cli/Cargo.toml index 52cccb65d..76bea2bfc 100644 --- a/crates/prisma-cli/Cargo.toml +++ b/crates/prisma-cli/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] prisma-client-rust-cli = { workspace = true } +sd-sync-generator = { path = "../sync-generator" } diff --git a/crates/prisma-cli/src/main.rs b/crates/prisma-cli/src/bin/prisma.rs similarity index 100% rename from crates/prisma-cli/src/main.rs rename to crates/prisma-cli/src/bin/prisma.rs diff --git a/crates/prisma-cli/src/bin/sync.rs b/crates/prisma-cli/src/bin/sync.rs new file mode 100644 index 000000000..58b173755 --- /dev/null +++ b/crates/prisma-cli/src/bin/sync.rs @@ -0,0 +1,3 @@ +fn main() { + sd_sync_generator::run(); +} diff --git a/crates/sync-generator/Cargo.toml b/crates/sync-generator/Cargo.toml new file mode 100644 index 000000000..f2968467d --- /dev/null +++ b/crates/sync-generator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "sd-sync-generator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nom = "7.1.1" +once_cell = "1.16.0" +prisma-client-rust-sdk = { workspace = true } +proc-macro2 = "1.0.47" +quote = "1.0.21" +serde = { version = "1.0.147", features = ["derive"] } +thiserror = "1.0.37" diff --git a/crates/sync/src/attribute/mod.rs b/crates/sync-generator/src/attribute/mod.rs similarity index 100% rename from crates/sync/src/attribute/mod.rs rename to crates/sync-generator/src/attribute/mod.rs diff --git a/crates/sync/src/attribute/parser.rs b/crates/sync-generator/src/attribute/parser.rs similarity index 100% rename from crates/sync/src/attribute/parser.rs rename to crates/sync-generator/src/attribute/parser.rs diff --git a/crates/sync-generator/src/client.rs b/crates/sync-generator/src/client.rs new file mode 100644 index 000000000..98bc5f26e --- /dev/null +++ b/crates/sync-generator/src/client.rs @@ -0,0 +1,38 @@ +use crate::prelude::*; + +pub fn r#struct(datamodel: &dml::Datamodel) -> TokenStream { + let model_action_fns = datamodel.models.iter().map(|model| { + let model_name_snake = snake_ident(&model.name); + let model_actions_struct = quote!(super::#model_name_snake::Actions); + + quote! { + pub fn #model_name_snake(&self) -> #model_actions_struct { + #model_actions_struct { client: self } + } + } + }); + + quote! { + pub struct PrismaCRDTClient { + pub(super) client: #PRISMA::PrismaClient, + pub node_id: Vec, + operation_sender: #MPSC::Sender<#SYNC::CRDTOperation> + } + + impl PrismaCRDTClient { + pub(super) fn _new( + client: #PRISMA::PrismaClient, + node_id: Vec, + operation_sender: #MPSC::Sender<#SYNC::CRDTOperation> + ) -> Self { + Self { + client, + node_id, + operation_sender, + } + } + + #(#model_action_fns)* + } + } +} diff --git a/crates/sync/src/datamodel/field.rs b/crates/sync-generator/src/datamodel/field.rs similarity index 100% rename from crates/sync/src/datamodel/field.rs rename to crates/sync-generator/src/datamodel/field.rs diff --git a/crates/sync/src/datamodel/mod.rs b/crates/sync-generator/src/datamodel/mod.rs similarity index 100% rename from crates/sync/src/datamodel/mod.rs rename to crates/sync-generator/src/datamodel/mod.rs diff --git a/crates/sync/src/datamodel/model.rs b/crates/sync-generator/src/datamodel/model.rs similarity index 100% rename from crates/sync/src/datamodel/model.rs rename to crates/sync-generator/src/datamodel/model.rs diff --git a/crates/sync/src/generator/client.rs b/crates/sync-generator/src/generator/client.rs similarity index 100% rename from crates/sync/src/generator/client.rs rename to crates/sync-generator/src/generator/client.rs diff --git a/crates/sync/src/generator/mod.rs b/crates/sync-generator/src/generator/mod.rs similarity index 100% rename from crates/sync/src/generator/mod.rs rename to crates/sync-generator/src/generator/mod.rs diff --git a/crates/sync/src/generator/model/actions.rs b/crates/sync-generator/src/generator/model/actions.rs similarity index 100% rename from crates/sync/src/generator/model/actions.rs rename to crates/sync-generator/src/generator/model/actions.rs diff --git a/crates/sync/src/generator/model/create.rs b/crates/sync-generator/src/generator/model/create.rs similarity index 100% rename from crates/sync/src/generator/model/create.rs rename to crates/sync-generator/src/generator/model/create.rs diff --git a/crates/sync/src/generator/model/create_params.rs b/crates/sync-generator/src/generator/model/create_params.rs similarity index 100% rename from crates/sync/src/generator/model/create_params.rs rename to crates/sync-generator/src/generator/model/create_params.rs diff --git a/crates/sync/src/generator/model/delete.rs b/crates/sync-generator/src/generator/model/delete.rs similarity index 100% rename from crates/sync/src/generator/model/delete.rs rename to crates/sync-generator/src/generator/model/delete.rs diff --git a/crates/sync/src/generator/model/mod.rs b/crates/sync-generator/src/generator/model/mod.rs similarity index 100% rename from crates/sync/src/generator/model/mod.rs rename to crates/sync-generator/src/generator/model/mod.rs diff --git a/crates/sync/src/generator/model/owned/mod.rs b/crates/sync-generator/src/generator/model/owned/mod.rs similarity index 100% rename from crates/sync/src/generator/model/owned/mod.rs rename to crates/sync-generator/src/generator/model/owned/mod.rs diff --git a/crates/sync/src/generator/model/relation/mod.rs b/crates/sync-generator/src/generator/model/relation/mod.rs similarity index 100% rename from crates/sync/src/generator/model/relation/mod.rs rename to crates/sync-generator/src/generator/model/relation/mod.rs diff --git a/crates/sync/src/generator/model/set_param.rs b/crates/sync-generator/src/generator/model/set_param.rs similarity index 100% rename from crates/sync/src/generator/model/set_param.rs rename to crates/sync-generator/src/generator/model/set_param.rs diff --git a/crates/sync/src/generator/model/shared/mod.rs b/crates/sync-generator/src/generator/model/shared/mod.rs similarity index 100% rename from crates/sync/src/generator/model/shared/mod.rs rename to crates/sync-generator/src/generator/model/shared/mod.rs diff --git a/crates/sync/src/generator/model/sync_id.rs b/crates/sync-generator/src/generator/model/sync_id.rs similarity index 100% rename from crates/sync/src/generator/model/sync_id.rs rename to crates/sync-generator/src/generator/model/sync_id.rs diff --git a/crates/sync/src/generator/model/update.rs b/crates/sync-generator/src/generator/model/update.rs similarity index 100% rename from crates/sync/src/generator/model/update.rs rename to crates/sync-generator/src/generator/model/update.rs diff --git a/crates/sync-generator/src/lib.rs b/crates/sync-generator/src/lib.rs new file mode 100644 index 000000000..b4b584744 --- /dev/null +++ b/crates/sync-generator/src/lib.rs @@ -0,0 +1,114 @@ +pub use prisma_client_rust_sdk::prelude::*; + +#[derive(Debug, serde::Serialize, thiserror::Error)] +enum Error {} + +#[derive(serde::Deserialize)] +struct SDSyncGenerator {} + +impl PrismaGenerator for SDSyncGenerator { + const NAME: &'static str = "SD Sync Generator"; + const DEFAULT_OUTPUT: &'static str = "prisma-sync.rs"; + + type Error = Error; + + fn generate(self, args: GenerateArgs) -> Result { + let set_param_impls = args.dml.models().map(|model| { + let model_name_snake = snake_ident(&model.name); + + let field_matches = model.fields().filter_map(|field| { + let field_name_snake = snake_ident(field.name()); + let field_name_snake_str = field_name_snake.to_string(); + + match field { + dml::Field::ScalarField(_) => { + Some(quote! { + #field_name_snake_str => crate::prisma::#model_name_snake::#field_name_snake::set(::serde_json::from_value(val).unwrap()), + }) + }, + dml::Field::RelationField(relation_field) => { + let relation_model_name_snake = snake_ident(&relation_field.relation_info.referenced_model); + + match &relation_field.relation_info.references[..] { + [_] => { + Some(quote! { + #field_name_snake_str => { + let val: std::collections::HashMap = ::serde_json::from_value(val).unwrap(); + let val = val.into_iter().next().unwrap(); + + crate::prisma::#model_name_snake::#field_name_snake::connect( + crate::prisma::#relation_model_name_snake::UniqueWhereParam::deserialize(&val.0, val.1).unwrap() + ) + }, + }) + }, + _ => None + } + }, + _ => None + } + }); + + match field_matches.clone().count() { + 0 => quote!(), + _ => quote! { + impl crate::prisma::#model_name_snake::SetParam { + pub fn deserialize(field: &str, val: ::serde_json::Value) -> Option { + Some(match field { + #(#field_matches)* + _ => return None + }) + } + } + } + } + }); + + let unique_where_param_impls = args.dml.models().map(|model| { + let model_name_snake = snake_ident(&model.name); + + let field_matches = model + .loose_unique_criterias() + .iter() + .flat_map(|criteria| match &criteria.fields[..] { + [field] => { + let unique_field_name_str = &field.name; + let unique_field_name_snake = snake_ident(unique_field_name_str); + + Some(quote!(#unique_field_name_str => + crate::prisma::#model_name_snake::#unique_field_name_snake::equals( + ::serde_json::from_value(val).unwrap() + ), + )) + } + _ => None, + }) + .collect::>(); + + match field_matches.len() { + 0 => quote!(), + _ => quote! { + impl crate::prisma::#model_name_snake::UniqueWhereParam { + pub fn deserialize(field: &str, val: ::serde_json::Value) -> Option { + Some(match field { + #(#field_matches)* + _ => return None + }) + } + } + }, + } + }); + + Ok(quote! { + #(#set_param_impls)* + + #(#unique_where_param_impls)* + } + .to_string()) + } +} + +pub fn run() { + SDSyncGenerator::run(); +} diff --git a/crates/sync-generator/src/model.rs b/crates/sync-generator/src/model.rs new file mode 100644 index 000000000..f740a79a4 --- /dev/null +++ b/crates/sync-generator/src/model.rs @@ -0,0 +1,96 @@ +use crate::{attribute::Attribute, prelude::*}; + +pub enum ModelType { + Owned, + SharedUnique, + SharedAtomic, + Relation, +} + +pub fn model_sync_type(model: &dml::Model, dml: &dml::Datamodel) -> ModelType { + let type_attribute = model + .documentation + .as_ref() + .map(Attribute::parse) + .unwrap() + .unwrap(); + + match type_attribute.name { + "owned" => ModelType::Owned, + "shared" => ModelType::SharedUnique, // TODO: fix + "relation" => ModelType::Relation, + _ => unreachable!(), + } +} +pub fn module(model: &dml::Model, dml: &dml::Datamodel) -> TokenStream { + let model_name_snake = snake_ident(&model.name); + + let set_params_enum = set_params_enum(model, dml); + + let actions_struct = actions_struct(model, dml); + + quote! { + pub mod #model_name_snake { + #set_params_enum + + #actions_struct + } + } +} + +pub fn set_params_enum(model: &dml::Model, dml: &dml::Datamodel) -> TokenStream { + quote! { + pub enum SetParam {} + } +} + +pub fn create_fn(model: &dml::Model, dml: &dml::Datamodel) -> TokenStream { + let required_scalar_fields = model.required_scalar_fields(); + + let args = required_scalar_fields.iter().map(|field| { + let name = snake_ident(field.name()); + let typ = field.type_tokens(quote!(crate::prisma::)); + + quote!(#name: #typ) + }); + + match model_sync_type(model, dml) { + ModelType::Owned => { + quote! { + pub fn create(&self, #(#args),*, _params: Vec) { + + } + } + } + ModelType::SharedUnique => { + quote! { + pub fn create(&self, #(#args),*, _params: Vec) {} + } + } + ModelType::SharedAtomic => { + quote! { + pub fn create(&self, _params: Vec) {} + } + } + ModelType::Relation => { + quote! { + pub fn create(&self, _params: Vec) {} + } + } + _ => todo!(), + } +} + +pub fn actions_struct(model: &dml::Model, dml: &dml::Datamodel) -> TokenStream { + let create_fn = create_fn(model, dml); + + quote! { + pub struct Actions<'a> { + pub(super) client: &'a super::#CRDT_CLIENT + } + + impl<'a> Actions<'a> { + #create_fn + } + } +} diff --git a/crates/sync/Cargo.toml b/crates/sync/Cargo.toml index 11c7869a2..d88cc7a3a 100644 --- a/crates/sync/Cargo.toml +++ b/crates/sync/Cargo.toml @@ -11,3 +11,5 @@ serde = "1.0.145" serde_json = "1.0.85" uhlc = "0.5.1" uuid = { version = "1.1.2", features = ["serde", "v4"] } +prisma-client-rust = { workspace = true } +serde-value = "0.7.0" diff --git a/crates/sync/docs/HLC.md b/crates/sync/docs/HLC.md deleted file mode 100644 index f4a947511..000000000 --- a/crates/sync/docs/HLC.md +++ /dev/null @@ -1,73 +0,0 @@ -```rust -pub fn update_with_timestamp(&self, timestamp: &Timestamp) -> Result<(), String> { - let mut now = (self.clock)(); - now.0 &= LMASK; - let msg_time = timestamp.get_time(); - if *msg_time > now && *msg_time - now > self.delta { - let err_msg = format!( - "incoming timestamp from {} exceeding delta {}ms is rejected: {} vs. now: {}", - timestamp.get_id(), - self.delta.to_duration().as_millis(), - msg_time, - now - ); - warn!("{}", err_msg); - Err(err_msg) - } else { - let mut last_time = lock!(self.last_time); - let max_time = cmp::max(cmp::max(now, *msg_time), *last_time); - if max_time == now { - *last_time = now; - } else if max_time == *msg_time { - *last_time = *msg_time + 1; - } else { - *last_time += 1; - } - Ok(()) - } -} -``` - -```javascript -Timestamp.recv = function (msg) { - if (!clock) { - return null; - } - - var now = Date.now(); - - var msg_time = msg.millis(); - var msg_time = msg.counter(); - - if (msg_time - now > config.maxDrift) { - throw new Timestamp.ClockDriftError(); - } - - var last_time = clock.timestamp.millis(); - var last_time = clock.timestamp.counter(); - - var max_time = Math.max(Math.max(last_time, now), msg_time); - - var last_time = - max_time === last_time && lNew === msg_time - ? Math.max(last_time, msg_time) + 1 - : max_time === last_time - ? last_time + 1 - : max_time === msg_time - ? msg_time + 1 - : 0; - - // 3. - if (max_time - phys > config.maxDrift) { - throw new Timestamp.ClockDriftError(); - } - if (last_time > MAX_COUNTER) { - throw new Timestamp.OverflowError(); - } - - clock.timestamp.setMillis(max_time); - clock.timestamp.setCounter(last_time); - - return new Timestamp(clock.timestamp.millis(), clock.timestamp.counter(), clock.timestamp.node()); -}; -``` diff --git a/crates/sync/docs/index.md b/crates/sync/docs/index.md deleted file mode 100644 index d83395995..000000000 --- a/crates/sync/docs/index.md +++ /dev/null @@ -1,115 +0,0 @@ -# Owned Records - -Node which owns the record is the sole source of truth for that record's state - -# Shared Records - -This includes Shared Record O-M Relations, since the foreign key is stored on the Many record. - -## Create - -```json -{ - "type": "CREATE", - "recordId": "{uuid}", - "model": "{model}", - "data": { - "key": "value" - }, - "node": "{uuid}", - "timestamp": { - "hybrid": "logical clock" - } -} -``` - -## Update - -```json -{ - "type": "UPDATE", - "recordId": "{uuid}", - "field": "{field}", - "value": "{value}", - "node": "{uuid}", - "timestamp": { - "hybrid": "logical clock" - } -} -``` - -## Delete - -```json -{ - "type": "DELETE", - "recordId": "{uuid}", - "node": "{uuid}", - "timestamp": { - "hybrid": "logical clock" - } -} -``` - -# Shared Record M-M Relations - -x-M relations usually signify an item belonging to a group. 1-M relations are handled normally by Shared Records since the ID of the record is just the ID of the M record. M-M relations require custom handling since they are identified by the two records they join, so 2 create instructions - -UNANSWERED: M-M relations that _can_ be duplicated. In this case, a single ID for the relation would suffice, in the same way that 1-M relations do. - -NOTE: Ordering is very important when syncing relations. If a target of a relation doesn't exist, what should happen? This presents two options: - -1. Don't use a foreign key, just join/fetch separately on a possibly non-existent foreign id. This is pretty cringe since Prisma only affords the niceties of relations if foreign keys are actually used. -2. Require that all operations are synced in order, independent of which node they were created on. This is nicer since it means that in order for a node to create a relation in the first place it must possess a message indicating creation of the relation target, but it is much more difficult to coordinate deletion of messages this way. Probably still doable though. - -Option 2 is probably the best way to go, since having to do annoying joins and losing database ergonomics is not great. Additionally, option 2 would result in the ability to sync shared data between any two nodes, even if the node being synced from didn't create the operation in the first place. - -## Create - -```json -{ - "type": "CREATE", - // Record that is being assigned to a group eg. a file - "relationItem": "{uuid}", - // Group that the record is being assigned to eg. a photo album - "relationGroup": "{uuid}", - // Name of the model which represents the relation - "relation": "model", - "node": "{uuid}", - "timestamp": { - "hybrid": "logical clock" - } -} -``` - -## Update - -```json -{ - "type": "UPDATE", - "relationItem": "{uuid}", - "relationGroup": "{uuid}", - "relation": "model", - "field": "field", - "value": "{value}", - "node": "{uuid}", - "timestamp": { - "hybrid": "logical clock" - } -} -``` - -## Delete - -```json -{ - "type": "DELETE", - "relationItem": "{uuid}", - "relationGroup": "{uuid}", - "relation": "relation", - "node": "{uuid}", - "timestamp": { - "hybrid": "logical clock" - } -} -``` diff --git a/crates/sync/docs/prisma-schema.md b/crates/sync/docs/prisma-schema.md deleted file mode 100644 index cb14b555f..000000000 --- a/crates/sync/docs/prisma-schema.md +++ /dev/null @@ -1,52 +0,0 @@ -# Prisma Schema - -`prisma-crdt` introduces new attributes that must be applied to fields and models via triple slash documentation comments. - -_Sync ID_: As well as having a primary key - denoted in Prisma with the `@id` field attribute - `prisma-crdt` introduces another ID - the _Sync ID_. -A model's Sync ID defaults to its regular ID, and is what identifies a model inside a sync operation. -Regular IDs are often not suitable for use inside a sync operation, as they may not be unique when sent to other nodes - eg. autoincrementing IDs - so something more unique can used, like a UUID. - -## Model Attributes - -#### `@local` - -Model that is entirely disconnected from sync. - -_Arguments_ - -- `id` (optional): Scalar field to override the default Sync ID. - -#### `@owned` - -Model that is synced via replicating from its owner to all other nodes, with the other nodes treating the model's owner as its single source of truth. - -_Arguments_ - -- `owner`: Field that identifies the owner of this model. If a scalar, will directly use that value in sync operations. If a relation, the Sync ID of the related model will be resolved for sync operations. -- `id` (optional): Scalar field to override the default Sync ID. - -#### `@shared` - -Model that is synced via updates on a per-field basis. - -_Arguments_ - -- `id` (optional): Scalar field to override the default Sync ID. -- `create` (optional): How the model should be created. - - `Unique` (default): Model can be created with many required arguemnts, but ID provided _must_ be unique across all nodes. Useful for Tags since their IDs are non-deterministic. - - `Atomic`: Require the model to have no required arguments apart from ID and apply all create arguments as atomic updates. Necessary for models with the same ID that can be created on multiple nodes. Useful for Files since their ID is dependent on their content, and could be the same across nodes. - -#### `@relation` - -Similar to `@shared`, but identified by the two records that it relates. Sync ID is always the combination of `item` and `group`. - -_Arguments_ - -- `item`: Field that identifies the item that the relation links to. Operates like the `owner` argument of `@owned`. -- `group`: Field that identifies the group that the item should be related to. Operates like the `owner` argument of `@owned`. - -## Field Attributes - -#### `@node` - -A relation whose value is automatically set to the current node. This could be done manually, but `@node` allows `node_id` fields to not be stored in `OwnedOperationData`, but rather be resolved from the `node_id` field of a `CRDTOperation`, saving on bandwidth. diff --git a/crates/sync/example/api/Cargo.toml b/crates/sync/example/Cargo.toml similarity index 78% rename from crates/sync/example/api/Cargo.toml rename to crates/sync/example/Cargo.toml index e91ec76f9..a000fbd32 100644 --- a/crates/sync/example/api/Cargo.toml +++ b/crates/sync/example/Cargo.toml @@ -1,6 +1,5 @@ [package] -name = "example-2" -default-run = "example-2" +name = "sd-sync-example" version = "0.1.0" edition = "2021" rust-version = "1.64" @@ -12,9 +11,9 @@ serde = { version = "1.0.145", features = ["derive"] } axum = "0.5.16" rspc = { workspace = true, features = ["axum"] } tokio = { version = "1.21.2", features = ["full"] } -# prisma-client-rust = { workspace = true } +prisma-client-rust = { workspace = true } dotenv = "0.15.0" tower-http = { version = "0.3.4", features = ["cors"] } -sd-sync = { path = "../.." } +sd-sync = { path = ".." } uuid = { version = "1.1.2", features = ["v4"] } http = "0.2.8" diff --git a/crates/sync/example/api/src/prisma.rs b/crates/sync/example/api/src/prisma.rs deleted file mode 100644 index 2552d4767..000000000 --- a/crates/sync/example/api/src/prisma.rs +++ /dev/null @@ -1,1037 +0,0 @@ -// Code generated by Prisma Client Rust. DO NOT EDIT - -#![allow(warnings, unused)] -pub static DATAMODEL_STR : & 'static str = include_str ! ("/Users/brendonovich/github.com/spacedriveapp/spacedrive/crates/sync/example-2/prisma/schema.prisma") ; -static DATABASE_STR: &'static str = "sqlite"; -use ::prisma_client_rust::migrations::include_dir; -pub static MIGRATIONS_DIR : & :: prisma_client_rust :: migrations :: include_dir :: Dir = & :: prisma_client_rust :: migrations :: include_dir :: include_dir ! ("/Users/brendonovich/github.com/spacedriveapp/spacedrive/crates/sync/example-2/prisma/migrations") ; -pub async fn new_client() -> Result { - let config = ::prisma_client_rust::datamodel::parse_configuration(DATAMODEL_STR)?.subject; - let source = config - .datasources - .first() - .expect("Please supply a datasource in your schema.prisma file"); - let url = if let Some(url) = source.load_shadow_database_url()? { - url - } else { - source.load_url(|key| std::env::var(key).ok())? - }; - let url = if url.starts_with("file:") { - let path = url.split(":").nth(1).unwrap(); - if std::path::Path::new("./schema.prisma").exists() { - url - } else if std::path::Path::new("./prisma/schema.prisma").exists() { - format!("file:./prisma/{}", path) - } else { - url - } - } else { - url - }; - new_client_with_url(&url).await -} -pub async fn new_client_with_url( - url: &str, -) -> Result { - let config = ::prisma_client_rust::datamodel::parse_configuration(DATAMODEL_STR)?.subject; - let source = config - .datasources - .first() - .expect("Please supply a datasource in your schema.prisma file"); - let (db_name, executor) = - ::prisma_client_rust::query_core::executor::load(&source, &[], &url).await?; - let internal_model = - ::prisma_client_rust::prisma_models::InternalDataModelBuilder::new(DATAMODEL_STR) - .build(db_name); - let query_schema = std::sync::Arc::new(prisma_client_rust::query_core::schema_builder::build( - internal_model, - true, - source.capabilities(), - vec![], - source.referential_integrity(), - )); - executor.primary_connector().get_connection().await?; - let url = url.to_string(); - Ok(PrismaClient::_new(executor, query_schema, url)) -} -pub mod user { - use super::_prisma::*; - use super::*; - pub mod id { - use super::super::*; - use super::_prisma::*; - use super::{OrderByParam, SetParam, UniqueWhereParam, WhereParam, WithParam}; - pub fn set>(value: i32) -> T { - Set(value).into() - } - pub fn equals>(value: i32) -> T { - UniqueWhereParam::IdEquals(value).into() - } - pub fn order(direction: ::prisma_client_rust::Direction) -> OrderByParam { - OrderByParam::Id(direction) - } - pub fn in_vec(value: Vec) -> WhereParam { - WhereParam::IdInVec(value) - } - pub fn not_in_vec(value: Vec) -> WhereParam { - WhereParam::IdNotInVec(value) - } - pub fn lt(value: i32) -> WhereParam { - WhereParam::IdLt(value) - } - pub fn lte(value: i32) -> WhereParam { - WhereParam::IdLte(value) - } - pub fn gt(value: i32) -> WhereParam { - WhereParam::IdGt(value) - } - pub fn gte(value: i32) -> WhereParam { - WhereParam::IdGte(value) - } - pub fn not(value: i32) -> WhereParam { - WhereParam::IdNot(value) - } - pub fn increment(value: i32) -> SetParam { - SetParam::IncrementId(value) - } - pub fn decrement(value: i32) -> SetParam { - SetParam::DecrementId(value) - } - pub fn multiply(value: i32) -> SetParam { - SetParam::MultiplyId(value) - } - pub fn divide(value: i32) -> SetParam { - SetParam::DivideId(value) - } - pub struct Set(pub i32); - impl From for SetParam { - fn from(value: Set) -> Self { - Self::SetId(value.0) - } - } - pub struct Include; - impl Into for Include { - fn into(self) -> super::IncludeParam { - super::IncludeParam::Id(self) - } - } - impl Include { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - ::prisma_client_rust::Selection::builder("id").build() - } - } - pub struct Select; - impl Into for Select { - fn into(self) -> super::SelectParam { - super::SelectParam::Id(self) - } - } - impl Select { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - ::prisma_client_rust::Selection::builder("id").build() - } - } - } - pub mod name { - use super::super::*; - use super::_prisma::*; - use super::{OrderByParam, SetParam, UniqueWhereParam, WhereParam, WithParam}; - pub fn set>(value: String) -> T { - Set(value).into() - } - pub fn equals(value: String) -> WhereParam { - WhereParam::NameEquals(value).into() - } - pub fn order(direction: ::prisma_client_rust::Direction) -> OrderByParam { - OrderByParam::Name(direction) - } - pub fn in_vec(value: Vec) -> WhereParam { - WhereParam::NameInVec(value) - } - pub fn not_in_vec(value: Vec) -> WhereParam { - WhereParam::NameNotInVec(value) - } - pub fn lt(value: String) -> WhereParam { - WhereParam::NameLt(value) - } - pub fn lte(value: String) -> WhereParam { - WhereParam::NameLte(value) - } - pub fn gt(value: String) -> WhereParam { - WhereParam::NameGt(value) - } - pub fn gte(value: String) -> WhereParam { - WhereParam::NameGte(value) - } - pub fn contains(value: String) -> WhereParam { - WhereParam::NameContains(value) - } - pub fn starts_with(value: String) -> WhereParam { - WhereParam::NameStartsWith(value) - } - pub fn ends_with(value: String) -> WhereParam { - WhereParam::NameEndsWith(value) - } - pub fn not(value: String) -> WhereParam { - WhereParam::NameNot(value) - } - pub struct Set(pub String); - impl From for SetParam { - fn from(value: Set) -> Self { - Self::SetName(value.0) - } - } - pub struct Include; - impl Into for Include { - fn into(self) -> super::IncludeParam { - super::IncludeParam::Name(self) - } - } - impl Include { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - ::prisma_client_rust::Selection::builder("name").build() - } - } - pub struct Select; - impl Into for Select { - fn into(self) -> super::SelectParam { - super::SelectParam::Name(self) - } - } - impl Select { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - ::prisma_client_rust::Selection::builder("name").build() - } - } - } - pub mod email { - use super::super::*; - use super::_prisma::*; - use super::{OrderByParam, SetParam, UniqueWhereParam, WhereParam, WithParam}; - pub fn set>(value: String) -> T { - Set(value).into() - } - pub fn equals>(value: String) -> T { - UniqueWhereParam::EmailEquals(value).into() - } - pub fn order(direction: ::prisma_client_rust::Direction) -> OrderByParam { - OrderByParam::Email(direction) - } - pub fn in_vec(value: Vec) -> WhereParam { - WhereParam::EmailInVec(value) - } - pub fn not_in_vec(value: Vec) -> WhereParam { - WhereParam::EmailNotInVec(value) - } - pub fn lt(value: String) -> WhereParam { - WhereParam::EmailLt(value) - } - pub fn lte(value: String) -> WhereParam { - WhereParam::EmailLte(value) - } - pub fn gt(value: String) -> WhereParam { - WhereParam::EmailGt(value) - } - pub fn gte(value: String) -> WhereParam { - WhereParam::EmailGte(value) - } - pub fn contains(value: String) -> WhereParam { - WhereParam::EmailContains(value) - } - pub fn starts_with(value: String) -> WhereParam { - WhereParam::EmailStartsWith(value) - } - pub fn ends_with(value: String) -> WhereParam { - WhereParam::EmailEndsWith(value) - } - pub fn not(value: String) -> WhereParam { - WhereParam::EmailNot(value) - } - pub struct Set(pub String); - impl From for SetParam { - fn from(value: Set) -> Self { - Self::SetEmail(value.0) - } - } - pub struct Include; - impl Into for Include { - fn into(self) -> super::IncludeParam { - super::IncludeParam::Email(self) - } - } - impl Include { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - ::prisma_client_rust::Selection::builder("email").build() - } - } - pub struct Select; - impl Into for Select { - fn into(self) -> super::SelectParam { - super::SelectParam::Email(self) - } - } - impl Select { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - ::prisma_client_rust::Selection::builder("email").build() - } - } - } - pub fn _outputs() -> Vec<::prisma_client_rust::Selection> { - ["id", "name", "email"] - .into_iter() - .map(|o| { - let builder = ::prisma_client_rust::Selection::builder(o); - builder.build() - }) - .collect() - } - pub fn create( - name: String, - email: String, - _params: Vec, - ) -> (String, String, Vec) { - (name, email, _params) - } - pub fn create_unchecked( - name: String, - email: String, - _params: Vec, - ) -> (String, String, Vec) { - (name, email, _params) - } - #[macro_export] - macro_rules ! _select_user { ($ (($ ($ func_arg : ident : $ func_arg_ty : ty) , +) =>) ? $ module_name : ident { $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { # [allow (warnings)] pub mod $ module_name { $ crate :: prisma :: user :: select ! (@ definitions ; $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) +) ; pub struct Select (Vec < :: prisma_client_rust :: Selection >) ; impl :: prisma_client_rust :: select :: SelectType for Select { type Data = Data ; type ModelData = $ crate :: prisma :: user :: Data ; fn to_selections (self) -> Vec < :: prisma_client_rust :: Selection > { self . 0 } } use super :: * ; pub fn select ($ ($ ($ func_arg : $ func_arg_ty) , +) ?) -> Select { Select ($ crate :: prisma :: user :: select ! (@ selections_to_select_params ; : select { $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) + }) . into_iter () . map (| p | p . to_selection ()) . collect ()) } } } ; ({ $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { { $ crate :: prisma :: user :: select ! (@ definitions ; $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) +) ; pub struct Select (Vec < :: prisma_client_rust :: Selection >) ; impl :: prisma_client_rust :: select :: SelectType for Select { type Data = Data ; type ModelData = $ crate :: prisma :: user :: Data ; fn to_selections (self) -> Vec < :: prisma_client_rust :: Selection > { self . 0 } } Select ($ crate :: prisma :: user :: select ! (@ selections_to_select_params ; : select { $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) + }) . into_iter () . map (| p | p . to_selection ()) . collect ()) } } ; (@ definitions ; $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) +) => { # [allow (warnings)] enum Fields { id , name , email } # [allow (warnings)] impl Fields { fn selections () { $ (let _ = Fields :: $ field ;) + } } # [allow (warnings)] # [derive (std :: fmt :: Debug , Clone)] pub struct Data { $ (pub $ field : $ crate :: prisma :: user :: select ! (@ field_type ; $ field $ (: $ selection_mode { $ ($ selections) + }) ?) ,) + } impl :: serde :: Serialize for Data { fn serialize < S > (& self , serializer : S) -> Result < S :: Ok , S :: Error > where S : :: serde :: Serializer , { use :: serde :: ser :: SerializeStruct ; let mut state = serializer . serialize_struct ("Data" , [$ (stringify ! ($ field) ,) +] . len ()) ? ; $ (state . serialize_field ($ crate :: prisma :: user :: select ! (@ field_serde_name ; $ field) , & self . $ field) ? ;) * state . end () } } impl < 'de > :: serde :: Deserialize < 'de > for Data { fn deserialize < D > (deserializer : D) -> Result < Self , D :: Error > where D : :: serde :: Deserializer < 'de > , { # [allow (warnings)] enum Field { $ ($ field) , + , } impl < 'de > :: serde :: Deserialize < 'de > for Field { fn deserialize < D > (deserializer : D) -> Result < Field , D :: Error > where D : :: serde :: Deserializer < 'de > , { struct FieldVisitor ; impl < 'de > :: serde :: de :: Visitor < 'de > for FieldVisitor { type Value = Field ; fn expecting (& self , formatter : & mut :: std :: fmt :: Formatter) -> :: std :: fmt :: Result { formatter . write_str (concat ! ($ ($ crate :: prisma :: user :: select ! (@ field_serde_name ; $ field) , ", ") , +)) } fn visit_str < E > (self , value : & str) -> Result < Field , E > where E : :: serde :: de :: Error , { match value { $ ($ crate :: prisma :: user :: select ! (@ field_serde_name ; $ field) => Ok (Field :: $ field)) , * , _ => Err (:: serde :: de :: Error :: unknown_field (value , FIELDS)) , } } } deserializer . deserialize_identifier (FieldVisitor) } } struct DataVisitor ; impl < 'de > :: serde :: de :: Visitor < 'de > for DataVisitor { type Value = Data ; fn expecting (& self , formatter : & mut std :: fmt :: Formatter) -> std :: fmt :: Result { formatter . write_str ("struct Data") } fn visit_map < V > (self , mut map : V) -> Result < Data , V :: Error > where V : :: serde :: de :: MapAccess < 'de > , { $ (let mut $ field = None ;) * while let Some (key) = map . next_key () ? { match key { $ (Field :: $ field => { if $ field . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: prisma :: user :: select ! (@ field_serde_name ; $ field))) ; } $ field = Some (map . next_value () ?) ; }) * } } $ (let $ field = $ field . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: prisma :: user :: select ! (@ field_serde_name ; $ field))) ? ;) * Ok (Data { $ ($ field) , * }) } } const FIELDS : & 'static [& 'static str] = & ["id" , "name" , "email"] ; deserializer . deserialize_struct ("Data" , FIELDS , DataVisitor) } } impl prisma_client_rust :: rspc :: internal :: specta :: Type for Data { const NAME : & 'static str = "Data" ; fn inline (_opts : prisma_client_rust :: rspc :: internal :: specta :: DefOpts , _ : & [prisma_client_rust :: rspc :: internal :: specta :: DataType]) -> prisma_client_rust :: rspc :: internal :: specta :: DataType { prisma_client_rust :: rspc :: internal :: specta :: DataType :: Object (prisma_client_rust :: rspc :: internal :: specta :: ObjectType { name : "Data" . to_string () , tag : None , generics : vec ! [] , fields : vec ! [$ (prisma_client_rust :: rspc :: internal :: specta :: ObjectField { name : stringify ! ($ field) . to_string () , optional : false , ty : < $ crate :: prisma :: user :: select ! (@ field_type ; $ field $ (: $ selection_mode { $ ($ selections) + }) ?) as prisma_client_rust :: rspc :: internal :: specta :: Type > :: reference (prisma_client_rust :: rspc :: internal :: specta :: DefOpts { parent_inline : false , type_map : _opts . type_map } , & []) }) , *] , type_id : None }) } fn reference (_opts : prisma_client_rust :: rspc :: internal :: specta :: DefOpts , _ : & [prisma_client_rust :: rspc :: internal :: specta :: DataType]) -> prisma_client_rust :: rspc :: internal :: specta :: DataType { Self :: inline (_opts , & []) } fn definition (_opts : prisma_client_rust :: rspc :: internal :: specta :: DefOpts) -> prisma_client_rust :: rspc :: internal :: specta :: DataType { unreachable ! () } } $ ($ (pub mod $ field { $ crate :: prisma :: user :: select ! (@ field_module ; $ field : $ selection_mode { $ ($ selections) + }) ; }) ?) + } ; (@ field_type ; id) => { i32 } ; (@ field_type ; name) => { String } ; (@ field_type ; email) => { String } ; (@ field_type ; $ field : ident $ ($ tokens : tt) *) => { compile_error ! (stringify ! (Cannot select field nonexistent field $ field on model "User" , available fields are "id, name, email")) } ; (@ field_module ; $ ($ tokens : tt) *) => { } ; (@ selection_field_to_selection_param ; id) => { Into :: < $ crate :: prisma :: user :: SelectParam > :: into ($ crate :: prisma :: user :: id :: Select) } ; (@ selection_field_to_selection_param ; name) => { Into :: < $ crate :: prisma :: user :: SelectParam > :: into ($ crate :: prisma :: user :: name :: Select) } ; (@ selection_field_to_selection_param ; email) => { Into :: < $ crate :: prisma :: user :: SelectParam > :: into ($ crate :: prisma :: user :: email :: Select) } ; (@ selection_field_to_selection_param ; $ ($ tokens : tt) *) => { compile_error ! (stringify ! ($ ($ tokens) *)) } ; (@ selections_to_select_params ; : $ macro_name : ident { $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { [$ ($ crate :: prisma :: user :: $ macro_name ! (@ selection_field_to_selection_param ; $ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) ,) +] } ; (@ filters_to_args ;) => { vec ! [] } ; (@ filters_to_args ; $ ($ t : tt) *) => { $ ($ t) * } ; (@ field_serde_name ; id) => { "id" } ; (@ field_serde_name ; name) => { "name" } ; (@ field_serde_name ; email) => { "email" } ; } - pub use _select_user as select; - pub enum SelectParam { - Id(id::Select), - Name(name::Select), - Email(email::Select), - } - impl SelectParam { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - match self { - Self::Id(data) => data.to_selection(), - Self::Name(data) => data.to_selection(), - Self::Email(data) => data.to_selection(), - } - } - } - #[macro_export] - macro_rules ! _include_user { ($ (($ ($ func_arg : ident : $ func_arg_ty : ty) , +) =>) ? $ module_name : ident { $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { # [allow (warnings)] pub mod $ module_name { $ crate :: prisma :: user :: include ! (@ definitions ; $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) +) ; pub struct Include (Vec < :: prisma_client_rust :: Selection >) ; impl :: prisma_client_rust :: include :: IncludeType for Include { type Data = Data ; type ModelData = $ crate :: prisma :: user :: Data ; fn to_selections (self) -> Vec < :: prisma_client_rust :: Selection > { self . 0 } } use super :: * ; pub fn include ($ ($ ($ func_arg : $ func_arg_ty) , +) ?) -> Include { let mut selections = $ crate :: prisma :: user :: _outputs () ; selections . extend ($ crate :: prisma :: user :: include ! (@ selections_to_include_params ; : include { $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) + }) . into_iter () . map (| p | p . to_selection ())) ; Include (selections) } } } ; ({ $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { { $ crate :: prisma :: user :: include ! (@ definitions ; $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) +) ; pub struct Include (Vec < :: prisma_client_rust :: Selection >) ; impl :: prisma_client_rust :: include :: IncludeType for Include { type Data = Data ; type ModelData = $ crate :: prisma :: user :: Data ; fn to_selections (self) -> Vec < :: prisma_client_rust :: Selection > { self . 0 } } Include ({ let mut selections = $ crate :: prisma :: user :: _outputs () ; selections . extend ($ crate :: prisma :: user :: include ! (@ selections_to_include_params ; : include { $ ($ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) + }) . into_iter () . map (| p | p . to_selection ())) ; selections }) } } ; (@ definitions ; $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) +) => { # [allow (warnings)] enum Fields { } # [allow (warnings)] impl Fields { fn selections () { $ (let _ = Fields :: $ field ;) + } } # [allow (warnings)] # [derive (std :: fmt :: Debug , Clone)] pub struct Data { pub id : i32 , pub name : String , pub email : String , $ (pub $ field : $ crate :: prisma :: user :: include ! (@ field_type ; $ field $ (: $ selection_mode { $ ($ selections) + }) ?) ,) + } impl :: serde :: Serialize for Data { fn serialize < S > (& self , serializer : S) -> Result < S :: Ok , S :: Error > where S : :: serde :: Serializer , { use :: serde :: ser :: SerializeStruct ; let mut state = serializer . serialize_struct ("Data" , [$ (stringify ! ($ field) ,) + stringify ! (id) , stringify ! (name) , stringify ! (email)] . len ()) ? ; $ (state . serialize_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; $ field) , & self . $ field) ? ;) * state . serialize_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; id) , & self . id) ? ; state . serialize_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; name) , & self . name) ? ; state . serialize_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; email) , & self . email) ? ; state . end () } } impl < 'de > :: serde :: Deserialize < 'de > for Data { fn deserialize < D > (deserializer : D) -> Result < Self , D :: Error > where D : :: serde :: Deserializer < 'de > , { # [allow (warnings)] enum Field { $ ($ field) , + , id , name , email } impl < 'de > :: serde :: Deserialize < 'de > for Field { fn deserialize < D > (deserializer : D) -> Result < Field , D :: Error > where D : :: serde :: Deserializer < 'de > , { struct FieldVisitor ; impl < 'de > :: serde :: de :: Visitor < 'de > for FieldVisitor { type Value = Field ; fn expecting (& self , formatter : & mut :: std :: fmt :: Formatter) -> :: std :: fmt :: Result { formatter . write_str (concat ! ($ ($ crate :: prisma :: user :: include ! (@ field_serde_name ; $ field) , ", ") , + , $ crate :: prisma :: user :: include ! (@ field_serde_name ; id) , ", " , $ crate :: prisma :: user :: include ! (@ field_serde_name ; name) , ", " , $ crate :: prisma :: user :: include ! (@ field_serde_name ; email) , ", ")) } fn visit_str < E > (self , value : & str) -> Result < Field , E > where E : :: serde :: de :: Error , { match value { $ ($ crate :: prisma :: user :: include ! (@ field_serde_name ; $ field) => Ok (Field :: $ field)) , * , $ crate :: prisma :: user :: include ! (@ field_serde_name ; id) => Ok (Field :: id) , $ crate :: prisma :: user :: include ! (@ field_serde_name ; name) => Ok (Field :: name) , $ crate :: prisma :: user :: include ! (@ field_serde_name ; email) => Ok (Field :: email) , _ => Err (:: serde :: de :: Error :: unknown_field (value , FIELDS)) , } } } deserializer . deserialize_identifier (FieldVisitor) } } struct DataVisitor ; impl < 'de > :: serde :: de :: Visitor < 'de > for DataVisitor { type Value = Data ; fn expecting (& self , formatter : & mut std :: fmt :: Formatter) -> std :: fmt :: Result { formatter . write_str ("struct Data") } fn visit_map < V > (self , mut map : V) -> Result < Data , V :: Error > where V : :: serde :: de :: MapAccess < 'de > , { $ (let mut $ field = None ;) * let mut id = None ; let mut name = None ; let mut email = None ; while let Some (key) = map . next_key () ? { match key { Field :: id => { if id . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; id))) ; } id = Some (map . next_value () ?) ; } Field :: name => { if name . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; name))) ; } name = Some (map . next_value () ?) ; } Field :: email => { if email . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; email))) ; } email = Some (map . next_value () ?) ; } $ (Field :: $ field => { if $ field . is_some () { return Err (:: serde :: de :: Error :: duplicate_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; $ field))) ; } $ field = Some (map . next_value () ?) ; }) * } } $ (let $ field = $ field . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; $ field))) ? ;) * let id = id . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; id))) ? ; let name = name . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; name))) ? ; let email = email . ok_or_else (|| serde :: de :: Error :: missing_field ($ crate :: prisma :: user :: include ! (@ field_serde_name ; email))) ? ; Ok (Data { id , name , email , $ ($ field) , * }) } } const FIELDS : & 'static [& 'static str] = & ["id" , "name" , "email"] ; deserializer . deserialize_struct ("Data" , FIELDS , DataVisitor) } } impl prisma_client_rust :: rspc :: internal :: specta :: Type for Data { const NAME : & 'static str = "Data" ; fn inline (_opts : prisma_client_rust :: rspc :: internal :: specta :: DefOpts , _ : & [prisma_client_rust :: rspc :: internal :: specta :: DataType]) -> prisma_client_rust :: rspc :: internal :: specta :: DataType { prisma_client_rust :: rspc :: internal :: specta :: DataType :: Object (prisma_client_rust :: rspc :: internal :: specta :: ObjectType { name : "Data" . to_string () , tag : None , generics : vec ! [] , fields : vec ! [prisma_client_rust :: rspc :: internal :: specta :: ObjectField { name : stringify ! (id) . to_string () , optional : false , ty : < i32 as prisma_client_rust :: rspc :: internal :: specta :: Type > :: reference (prisma_client_rust :: rspc :: internal :: specta :: DefOpts { parent_inline : false , type_map : _opts . type_map } , & []) } , prisma_client_rust :: rspc :: internal :: specta :: ObjectField { name : stringify ! (name) . to_string () , optional : false , ty : < String as prisma_client_rust :: rspc :: internal :: specta :: Type > :: reference (prisma_client_rust :: rspc :: internal :: specta :: DefOpts { parent_inline : false , type_map : _opts . type_map } , & []) } , prisma_client_rust :: rspc :: internal :: specta :: ObjectField { name : stringify ! (email) . to_string () , optional : false , ty : < String as prisma_client_rust :: rspc :: internal :: specta :: Type > :: reference (prisma_client_rust :: rspc :: internal :: specta :: DefOpts { parent_inline : false , type_map : _opts . type_map } , & []) } , $ (prisma_client_rust :: rspc :: internal :: specta :: ObjectField { name : stringify ! ($ field) . to_string () , optional : false , ty : < $ crate :: prisma :: user :: include ! (@ field_type ; $ field $ (: $ selection_mode { $ ($ selections) + }) ?) as prisma_client_rust :: rspc :: internal :: specta :: Type > :: reference (prisma_client_rust :: rspc :: internal :: specta :: DefOpts { parent_inline : false , type_map : _opts . type_map } , & []) }) , *] , type_id : None }) } fn reference (_opts : prisma_client_rust :: rspc :: internal :: specta :: DefOpts , _ : & [prisma_client_rust :: rspc :: internal :: specta :: DataType]) -> prisma_client_rust :: rspc :: internal :: specta :: DataType { Self :: inline (_opts , & []) } fn definition (_opts : prisma_client_rust :: rspc :: internal :: specta :: DefOpts) -> prisma_client_rust :: rspc :: internal :: specta :: DataType { unreachable ! () } } $ ($ (pub mod $ field { $ crate :: prisma :: user :: $ selection_mode ! (@ field_module ; $ field : $ selection_mode { $ ($ selections) + }) ; }) ?) + } ; (@ field_type ; $ field : ident $ ($ tokens : tt) *) => { compile_error ! (stringify ! (Cannot include nonexistent relation $ field on model "User" , available relations are "")) } ; (@ field_module ; $ ($ tokens : tt) *) => { } ; (@ selection_field_to_selection_param ; $ ($ tokens : tt) *) => { compile_error ! (stringify ! ($ ($ tokens) *)) } ; (@ selections_to_include_params ; : $ macro_name : ident { $ ($ field : ident $ (($ ($ filters : tt) +) $ (. $ arg : ident ($ ($ arg_params : tt) *)) *) ? $ (: $ selection_mode : ident { $ ($ selections : tt) + }) ?) + }) => { [$ ($ crate :: prisma :: user :: $ macro_name ! (@ selection_field_to_selection_param ; $ field $ (($ ($ filters) +) $ (. $ arg ($ ($ arg_params) *)) *) ? $ (: $ selection_mode { $ ($ selections) + }) ?) ,) +] } ; (@ filters_to_args ;) => { vec ! [] } ; (@ filters_to_args ; $ ($ t : tt) *) => { $ ($ t) * } ; (@ field_serde_name ; id) => { "id" } ; (@ field_serde_name ; name) => { "name" } ; (@ field_serde_name ; email) => { "email" } ; } - pub use _include_user as include; - pub enum IncludeParam { - Id(id::Include), - Name(name::Include), - Email(email::Include), - } - impl IncludeParam { - pub fn to_selection(self) -> ::prisma_client_rust::Selection { - match self { - Self::Id(data) => data.to_selection(), - Self::Name(data) => data.to_selection(), - Self::Email(data) => data.to_selection(), - } - } - } - #[derive( - Debug, - Clone, - :: serde :: Serialize, - :: serde :: Deserialize, - :: prisma_client_rust :: rspc :: Type, - )] - #[specta(rename = "User", crate = "prisma_client_rust::rspc::internal::specta")] - pub struct Data { - #[serde(rename = "id")] - pub id: i32, - #[serde(rename = "name")] - pub name: String, - #[serde(rename = "email")] - pub email: String, - } - impl Data {} - #[derive(Clone)] - pub enum WithParam {} - impl Into<::prisma_client_rust::Selection> for WithParam { - fn into(self) -> ::prisma_client_rust::Selection { - match self {} - } - } - #[derive(Clone)] - pub enum SetParam { - SetId(i32), - IncrementId(i32), - DecrementId(i32), - MultiplyId(i32), - DivideId(i32), - SetName(String), - SetEmail(String), - } - impl Into<(String, ::prisma_client_rust::PrismaValue)> for SetParam { - fn into(self) -> (String, ::prisma_client_rust::PrismaValue) { - match self { - SetParam::SetId(value) => ( - "id".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - ), - SetParam::IncrementId(value) => ( - "id".to_string(), - ::prisma_client_rust::PrismaValue::Object(vec![( - "increment".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - SetParam::DecrementId(value) => ( - "id".to_string(), - ::prisma_client_rust::PrismaValue::Object(vec![( - "decrement".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - SetParam::MultiplyId(value) => ( - "id".to_string(), - ::prisma_client_rust::PrismaValue::Object(vec![( - "multiply".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - SetParam::DivideId(value) => ( - "id".to_string(), - ::prisma_client_rust::PrismaValue::Object(vec![( - "divide".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - SetParam::SetName(value) => ( - "name".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - ), - SetParam::SetEmail(value) => ( - "email".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - ), - } - } - } - #[derive(Clone)] - pub enum OrderByParam { - Id(::prisma_client_rust::Direction), - Name(::prisma_client_rust::Direction), - Email(::prisma_client_rust::Direction), - } - impl Into<(String, ::prisma_client_rust::PrismaValue)> for OrderByParam { - fn into(self) -> (String, ::prisma_client_rust::PrismaValue) { - match self { - Self::Id(direction) => ( - "id".to_string(), - ::prisma_client_rust::PrismaValue::String(direction.to_string()), - ), - Self::Name(direction) => ( - "name".to_string(), - ::prisma_client_rust::PrismaValue::String(direction.to_string()), - ), - Self::Email(direction) => ( - "email".to_string(), - ::prisma_client_rust::PrismaValue::String(direction.to_string()), - ), - } - } - } - #[derive(Clone)] - pub enum WhereParam { - Not(Vec), - Or(Vec), - And(Vec), - IdEquals(i32), - IdInVec(Vec), - IdNotInVec(Vec), - IdLt(i32), - IdLte(i32), - IdGt(i32), - IdGte(i32), - IdNot(i32), - NameEquals(String), - NameInVec(Vec), - NameNotInVec(Vec), - NameLt(String), - NameLte(String), - NameGt(String), - NameGte(String), - NameContains(String), - NameStartsWith(String), - NameEndsWith(String), - NameNot(String), - EmailEquals(String), - EmailInVec(Vec), - EmailNotInVec(Vec), - EmailLt(String), - EmailLte(String), - EmailGt(String), - EmailGte(String), - EmailContains(String), - EmailStartsWith(String), - EmailEndsWith(String), - EmailNot(String), - } - impl Into<::prisma_client_rust::SerializedWhere> for WhereParam { - fn into(self) -> ::prisma_client_rust::SerializedWhere { - match self { - Self::Not(value) => ::prisma_client_rust::SerializedWhere::new( - "NOT", - ::prisma_client_rust::SerializedWhereValue::Object( - value - .into_iter() - .map(Into::<::prisma_client_rust::SerializedWhere>::into) - .map(Into::into) - .collect(), - ), - ), - Self::Or(value) => ::prisma_client_rust::SerializedWhere::new( - "OR", - ::prisma_client_rust::SerializedWhereValue::List( - value - .into_iter() - .map(Into::<::prisma_client_rust::SerializedWhere>::into) - .map(Into::into) - .map(|v| vec![v]) - .map(::prisma_client_rust::PrismaValue::Object) - .collect(), - ), - ), - Self::And(value) => ::prisma_client_rust::SerializedWhere::new( - "AND", - ::prisma_client_rust::SerializedWhereValue::Object( - value - .into_iter() - .map(Into::<::prisma_client_rust::SerializedWhere>::into) - .map(Into::into) - .collect(), - ), - ), - Self::IdEquals(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "equals".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - Self::IdInVec(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "in".to_string(), - ::prisma_client_rust::PrismaValue::List( - value - .into_iter() - .map(|v| ::prisma_client_rust::PrismaValue::Int(v as i64)) - .collect(), - ), - )]), - ), - Self::IdNotInVec(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "notIn".to_string(), - ::prisma_client_rust::PrismaValue::List( - value - .into_iter() - .map(|v| ::prisma_client_rust::PrismaValue::Int(v as i64)) - .collect(), - ), - )]), - ), - Self::IdLt(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "lt".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - Self::IdLte(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "lte".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - Self::IdGt(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "gt".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - Self::IdGte(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "gte".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - Self::IdNot(value) => ::prisma_client_rust::SerializedWhere::new( - "id", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "not".to_string(), - ::prisma_client_rust::PrismaValue::Int(value as i64), - )]), - ), - Self::NameEquals(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "equals".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameInVec(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "in".to_string(), - ::prisma_client_rust::PrismaValue::List( - value - .into_iter() - .map(|v| ::prisma_client_rust::PrismaValue::String(v)) - .collect(), - ), - )]), - ), - Self::NameNotInVec(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "notIn".to_string(), - ::prisma_client_rust::PrismaValue::List( - value - .into_iter() - .map(|v| ::prisma_client_rust::PrismaValue::String(v)) - .collect(), - ), - )]), - ), - Self::NameLt(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "lt".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameLte(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "lte".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameGt(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "gt".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameGte(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "gte".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameContains(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "contains".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameStartsWith(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "startsWith".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameEndsWith(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "endsWith".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::NameNot(value) => ::prisma_client_rust::SerializedWhere::new( - "name", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "not".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailEquals(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "equals".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailInVec(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "in".to_string(), - ::prisma_client_rust::PrismaValue::List( - value - .into_iter() - .map(|v| ::prisma_client_rust::PrismaValue::String(v)) - .collect(), - ), - )]), - ), - Self::EmailNotInVec(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "notIn".to_string(), - ::prisma_client_rust::PrismaValue::List( - value - .into_iter() - .map(|v| ::prisma_client_rust::PrismaValue::String(v)) - .collect(), - ), - )]), - ), - Self::EmailLt(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "lt".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailLte(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "lte".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailGt(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "gt".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailGte(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "gte".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailContains(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "contains".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailStartsWith(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "startsWith".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailEndsWith(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "endsWith".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - Self::EmailNot(value) => ::prisma_client_rust::SerializedWhere::new( - "email", - ::prisma_client_rust::SerializedWhereValue::Object(vec![( - "not".to_string(), - ::prisma_client_rust::PrismaValue::String(value), - )]), - ), - } - } - } - #[derive(Clone)] - pub enum UniqueWhereParam { - EmailEquals(String), - IdEquals(i32), - } - impl From for WhereParam { - fn from(value: UniqueWhereParam) -> Self { - match value { - UniqueWhereParam::EmailEquals(value) => Self::EmailEquals(value), - UniqueWhereParam::IdEquals(value) => Self::IdEquals(value), - } - } - } - impl From<::prisma_client_rust::Operator> for WhereParam { - fn from(op: ::prisma_client_rust::Operator) -> Self { - match op { - ::prisma_client_rust::Operator::Not(value) => Self::Not(value), - ::prisma_client_rust::Operator::And(value) => Self::And(value), - ::prisma_client_rust::Operator::Or(value) => Self::Or(value), - } - } - } - pub type UniqueArgs = ::prisma_client_rust::UniqueArgs; - pub type ManyArgs = - ::prisma_client_rust::ManyArgs; - pub type Count<'a> = - ::prisma_client_rust::Count<'a, WhereParam, OrderByParam, UniqueWhereParam>; - pub type Create<'a> = ::prisma_client_rust::Create<'a, SetParam, WithParam, Data>; - pub type CreateMany<'a> = ::prisma_client_rust::CreateMany<'a, SetParam>; - pub type FindUnique<'a> = - ::prisma_client_rust::FindUnique<'a, WhereParam, WithParam, SetParam, Data>; - pub type FindMany<'a> = ::prisma_client_rust::FindMany< - 'a, - WhereParam, - WithParam, - OrderByParam, - UniqueWhereParam, - SetParam, - Data, - >; - pub type FindFirst<'a> = ::prisma_client_rust::FindFirst< - 'a, - WhereParam, - WithParam, - OrderByParam, - UniqueWhereParam, - Data, - >; - pub type Update<'a> = ::prisma_client_rust::Update<'a, WhereParam, WithParam, SetParam, Data>; - pub type UpdateMany<'a> = ::prisma_client_rust::UpdateMany<'a, WhereParam, SetParam>; - pub type Upsert<'a> = ::prisma_client_rust::Upsert<'a, WhereParam, SetParam, WithParam, Data>; - pub type Delete<'a> = ::prisma_client_rust::Delete<'a, WhereParam, WithParam, Data>; - pub type DeleteMany<'a> = ::prisma_client_rust::DeleteMany<'a, WhereParam>; - pub struct Actions<'a> { - pub client: &'a PrismaClient, - } - impl<'a> Actions<'a> { - pub fn find_unique(self, _where: UniqueWhereParam) -> FindUnique<'a> { - FindUnique::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where.into(), - ) - } - pub fn find_first(self, _where: Vec) -> FindFirst<'a> { - FindFirst::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where, - ) - } - pub fn find_many(self, _where: Vec) -> FindMany<'a> { - FindMany::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where, - ) - } - pub fn create(self, name: String, email: String, mut _params: Vec) -> Create<'a> { - _params.push(name::set(name)); - _params.push(email::set(email)); - Create::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _params, - ) - } - pub fn update(self, _where: UniqueWhereParam, _params: Vec) -> Update<'a> { - Update::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where.into(), - _params, - vec![], - ) - } - pub fn update_many( - self, - _where: Vec, - _params: Vec, - ) -> UpdateMany<'a> { - UpdateMany::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where, - _params, - ) - } - pub fn upsert( - self, - _where: UniqueWhereParam, - (name, email, mut _params): (String, String, Vec), - _update: Vec, - ) -> Upsert<'a> { - _params.push(name::set(name)); - _params.push(email::set(email)); - Upsert::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where.into(), - _params, - _update, - ) - } - pub fn delete(self, _where: UniqueWhereParam) -> Delete<'a> { - Delete::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where.into(), - vec![], - ) - } - pub fn delete_many(self, _where: Vec) -> DeleteMany<'a> { - DeleteMany::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - _where.into(), - ) - } - pub fn count(self, _where: Vec) -> Count<'a> { - Count::new( - self.client._new_query_context(), - ::prisma_client_rust::QueryInfo::new("User", _outputs()), - vec![], - ) - } - } -} -pub mod _prisma { - pub struct PrismaClient { - executor: ::prisma_client_rust::Executor, - query_schema: ::std::sync::Arc<::prisma_client_rust::schema::QuerySchema>, - url: String, - } - impl ::std::fmt::Debug for PrismaClient { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_struct("PrismaClient").finish() - } - } - impl PrismaClient { - pub(super) fn _new_query_context(&self) -> ::prisma_client_rust::queries::QueryContext { - ::prisma_client_rust::queries::QueryContext::new(&self.executor, &self.query_schema) - } - pub(super) fn _new( - executor: ::prisma_client_rust::Executor, - query_schema: std::sync::Arc<::prisma_client_rust::schema::QuerySchema>, - url: String, - ) -> Self { - Self { - executor, - query_schema, - url, - } - } - pub fn _query_raw( - &self, - query: ::prisma_client_rust::raw::Raw, - ) -> ::prisma_client_rust::QueryRaw { - ::prisma_client_rust::QueryRaw::new( - ::prisma_client_rust::queries::QueryContext::new( - &self.executor, - &self.query_schema, - ), - query, - super::DATABASE_STR, - ) - } - pub fn _execute_raw( - &self, - query: ::prisma_client_rust::raw::Raw, - ) -> ::prisma_client_rust::ExecuteRaw { - ::prisma_client_rust::ExecuteRaw::new( - ::prisma_client_rust::queries::QueryContext::new( - &self.executor, - &self.query_schema, - ), - query, - super::DATABASE_STR, - ) - } - pub async fn _batch, Marker>( - &self, - queries: T, - ) -> ::prisma_client_rust::queries::Result { - ::prisma_client_rust::batch(queries, &self.executor, &self.query_schema).await - } - pub async fn _migrate_deploy( - &self, - ) -> Result<(), ::prisma_client_rust::migrations::MigrateDeployError> { - let res = ::prisma_client_rust::migrations::migrate_deploy( - super::DATAMODEL_STR, - super::MIGRATIONS_DIR, - &self.url, - ) - .await; - tokio::time::sleep(core::time::Duration::from_millis(1)).await; - res - } - pub async fn _migrate_resolve( - &self, - migration: &str, - ) -> Result<(), ::prisma_client_rust::migrations::MigrateResolveError> { - ::prisma_client_rust::migrations::migrate_resolve( - migration, - super::DATAMODEL_STR, - super::MIGRATIONS_DIR, - &self.url, - ) - .await - } - pub fn _db_push(&self) -> ::prisma_client_rust::migrations::DbPush { - ::prisma_client_rust::migrations::db_push(super::DATAMODEL_STR, &self.url) - } - pub fn user(&self) -> super::user::Actions { - super::user::Actions { client: &self } - } - } - #[derive(Debug, Clone, Copy, :: serde :: Serialize, :: serde :: Deserialize)] - pub enum SortOrder { - #[serde(rename = "asc")] - Asc, - #[serde(rename = "desc")] - Desc, - } - impl ToString for SortOrder { - fn to_string(&self) -> String { - match self { - Self::Asc => "asc".to_string(), - Self::Desc => "desc".to_string(), - } - } - } - #[derive(Debug, Clone, Copy, :: serde :: Serialize, :: serde :: Deserialize)] - pub enum TransactionIsolationLevel { - #[serde(rename = "Serializable")] - Serializable, - } - impl ToString for TransactionIsolationLevel { - fn to_string(&self) -> String { - match self { - Self::Serializable => "Serializable".to_string(), - } - } - } - #[derive(Debug, Clone, Copy, :: serde :: Serialize, :: serde :: Deserialize)] - pub enum UserScalarFieldEnum { - #[serde(rename = "id")] - Id, - #[serde(rename = "name")] - Name, - #[serde(rename = "email")] - Email, - } - impl ToString for UserScalarFieldEnum { - fn to_string(&self) -> String { - match self { - Self::Id => "id".to_string(), - Self::Name => "name".to_string(), - Self::Email => "email".to_string(), - } - } - } -} -pub use _prisma::PrismaClient; diff --git a/crates/sync/example/prisma/Cargo.toml b/crates/sync/example/prisma/Cargo.toml deleted file mode 100644 index 94d564ad5..000000000 --- a/crates/sync/example/prisma/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "prisma-cli" -version = "0.1.0" -edition = "2021" - -[dependencies] -prisma-client-rust-cli = { workspace = true, features = [ - "migrations", -] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -tokio = { version = "1.13.0", features = ["full"] } diff --git a/crates/sync/example/prisma/schema.prisma b/crates/sync/example/prisma/schema.prisma index 5dde9159b..efc4ee911 100644 --- a/crates/sync/example/prisma/schema.prisma +++ b/crates/sync/example/prisma/schema.prisma @@ -8,11 +8,27 @@ datasource db { generator client { provider = "cargo prisma" - output = "../api/src/prisma.rs" + output = "../src/prisma.rs" } -model User { - id Int @id @default(autoincrement()) - name String - email String @unique +generator sync { + provider = "cargo run -p prisma-cli --bin sync --" + output = "../src/prisma_sync.rs" +} + +/// @owned +model FilePath { + id Bytes @id + path String + + object Object? @relation(fields: [object_id], references: [id]) + object_id Bytes? +} + +/// @shared +model Object { + id Bytes @id + name String + + paths FilePath[] @relation() } diff --git a/crates/sync/example/prisma/src/main.rs b/crates/sync/example/prisma/src/main.rs deleted file mode 100644 index f7580155a..000000000 --- a/crates/sync/example/prisma/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - prisma_client_rust_cli::run(); -} diff --git a/crates/sync/example/api/src/api/mod.rs b/crates/sync/example/src/api/mod.rs similarity index 84% rename from crates/sync/example/api/src/api/mod.rs rename to crates/sync/example/src/api/mod.rs index 008b23d88..14cc6d257 100644 --- a/crates/sync/example/api/src/api/mod.rs +++ b/crates/sync/example/src/api/mod.rs @@ -8,9 +8,11 @@ use std::path::PathBuf; use tokio::sync::Mutex; use uuid::Uuid; -#[derive(Default)] +use crate::prisma::{file_path, PrismaClient}; + pub struct Ctx { pub dbs: HashMap, + pub prisma: PrismaClient, } type Router = rspc::Router>>; @@ -25,8 +27,24 @@ fn to_map(v: &impl serde::Serialize) -> serde_json::Map { pub(crate) fn new() -> RouterBuilder>> { Router::new() .config(Config::new().export_ts_bindings( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../web/src/utils/bindings.ts"), + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("web/src/utils/bindings.ts"), )) + .mutation("testCreate", |r| { + r(|ctx, _: String| async move { + let prisma = &ctx.lock().await.prisma; + + let res = prisma + .file_path() + .create(vec![], String::new(), vec![]) + .exec_raw() + .await + .unwrap(); + + file_path::Create::operation_from_data(&res); + + Ok(()) + }) + }) .mutation("createDatabase", |r| { r(|ctx, _: String| async move { let dbs = &mut ctx.lock().await.dbs; @@ -99,7 +117,10 @@ pub(crate) fn new() -> RouterBuilder>> { model: "FilePath".to_string(), items: vec![OwnedOperationItem { id: serde_json::to_value(id).unwrap(), - data: OwnedOperationData::Create(to_map(&file_path)), + data: OwnedOperationData::Create( + serde_json::from_value(serde_json::to_value(&file_path).unwrap()) + .unwrap(), + ), }], })); diff --git a/crates/sync/example/api/src/main.rs b/crates/sync/example/src/main.rs similarity index 79% rename from crates/sync/example/api/src/main.rs rename to crates/sync/example/src/main.rs index b4b9182ed..a04eae216 100644 --- a/crates/sync/example/api/src/main.rs +++ b/crates/sync/example/src/main.rs @@ -1,3 +1,4 @@ +use api::Ctx; use axum::{ http::{HeaderValue, Method}, routing::get, @@ -7,13 +8,17 @@ use tokio::sync::Mutex; use tower_http::cors::CorsLayer; mod api; -// mod prisma; +mod prisma; +mod prisma_sync; mod utils; -fn router() -> axum::Router { +async fn router() -> axum::Router { let router = api::new().build().arced(); - let ctx = Arc::new(Mutex::new(Default::default())); + let ctx = Arc::new(Mutex::new(Ctx { + dbs: Default::default(), + prisma: prisma::new_client().await.unwrap(), + })); axum::Router::new() .route("/", get(|| async { "Hello 'rspc'!" })) @@ -33,7 +38,7 @@ async fn main() { let addr = "[::]:9000".parse::().unwrap(); // This listens on IPv6 and IPv4 println!("{} listening on http://{}", env!("CARGO_CRATE_NAME"), addr); axum::Server::bind(&addr) - .serve(router().into_make_service()) + .serve(router().await.into_make_service()) .with_graceful_shutdown(utils::axum_shutdown_signal()) .await .expect("Error with HTTP server!"); diff --git a/crates/sync/example/api/src/utils.rs b/crates/sync/example/src/utils.rs similarity index 100% rename from crates/sync/example/api/src/utils.rs rename to crates/sync/example/src/utils.rs diff --git a/crates/sync/example/web/src/App.tsx b/crates/sync/example/web/src/App.tsx index e3911b4bf..2ea420bc2 100644 --- a/crates/sync/example/web/src/App.tsx +++ b/crates/sync/example/web/src/App.tsx @@ -5,144 +5,154 @@ import { tests } from './test'; import { CRDTOperationType, rspc } from './utils/rspc'; export function App() { - const dbs = rspc.useQuery(['dbs', 'cringe']); + const dbs = rspc.useQuery(['dbs', 'cringe']); - const operations = rspc.useQuery(['operations', 'cringe']); + const operations = rspc.useQuery(['operations', 'cringe']); - const createDb = rspc.useMutation('createDatabase'); - const removeDbs = rspc.useMutation('removeDatabases'); + const createDb = rspc.useMutation('createDatabase'); + const removeDbs = rspc.useMutation('removeDatabases'); + const testCreate = rspc.useMutation('testCreate'); - return ( -
-
-
- - -
-
    - {Object.entries(tests).map(([key, test]) => ( -
  • - -
  • - ))} -
-
-
-
    - {dbs.data?.map((id) => ( - - - - ))} -
-
-
-

All Operations

-
    - {operations.data?.map((op) => ( -
  • -

    ID: {op.id}

    -

    Timestamp: {op.timestamp.toString()}

    -

    Node: {op.node}

    -
  • - ))} -
-
-
- ); + return ( +
+
+
+ + + +
+
    + {Object.entries(tests).map(([key, test]) => ( +
  • + +
  • + ))} +
+
+
+
    + {dbs.data?.map((id) => ( + + + + ))} +
+
+
+

All Operations

+
    + {operations.data?.map((op) => ( +
  • +

    ID: {op.id}

    +

    Timestamp: {op.timestamp.toString()}

    +

    Node: {op.node}

    +
  • + ))} +
+
+
+ ); } interface DatabaseViewProps { - id: string; + id: string; } const TABS = ['File Paths', 'Objects', 'Tags', 'Operations']; function DatabaseView(props: DatabaseViewProps) { - const [currentTab, setCurrentTab] = useState('Operations'); + const [currentTab, setCurrentTab] = useState('Operations'); - const pullOperations = rspc.useMutation('pullOperations'); + const pullOperations = rspc.useMutation('pullOperations'); - return ( -
-
-

{props.id}

- -
-
- - - {currentTab === 'File Paths' && } - {currentTab === 'Operations' && } - -
-
- ); + return ( +
+
+

{props.id}

+ +
+
+ + + {currentTab === 'File Paths' && } + {currentTab === 'Operations' && } + +
+
+ ); } function FilePathList(props: { db: string }) { - const createFilePath = rspc.useMutation('file_path.create'); - const filePaths = rspc.useQuery(['file_path.list', props.db]); + const createFilePath = rspc.useMutation('file_path.create'); + const filePaths = rspc.useQuery(['file_path.list', props.db]); - return ( -
- {filePaths.data && ( -
    - {filePaths.data.sort((a, b) => a.id.localeCompare(b.id)).map((path) => ( -
  • {JSON.stringify(path)}
  • - ))} -
- )} - -
- ); + return ( +
+ {filePaths.data && ( +
    + {filePaths.data + .sort((a, b) => a.id.localeCompare(b.id)) + .map((path) => ( +
  • {JSON.stringify(path)}
  • + ))} +
+ )} + +
+ ); } function messageType(msg: CRDTOperationType) { - if ('items' in msg) { - return 'Owned'; - } else if ('record_id' in msg) { - return 'Shared'; - } + if ('items' in msg) { + return 'Owned'; + } else if ('record_id' in msg) { + return 'Shared'; + } } function OperationList(props: { db: string }) { - const messages = rspc.useQuery(['message.list', props.db]); + const messages = rspc.useQuery(['message.list', props.db]); - return ( -
- {messages.data && ( - - {messages.data.sort((a, b) => Number(a.timestamp - b.timestamp)).map((message) => ( - - - - - - ))} -
{message.id}{new Date(Number(message.timestamp) / 10000000).toLocaleTimeString()}{messageType(message.typ)}
- )} -
- ); + return ( +
+ {messages.data && ( + + {messages.data + .sort((a, b) => Number(a.timestamp - b.timestamp)) + .map((message) => ( + + + + + + ))} +
{message.id} + {new Date(Number(message.timestamp) / 10000000).toLocaleTimeString()} + {messageType(message.typ)}
+ )} +
+ ); } const ButtonStyles = 'bg-blue-500 text-white px-2 py-1 rounded-md'; diff --git a/crates/sync/example/web/src/utils/bindings.ts b/crates/sync/example/web/src/utils/bindings.ts index f60d406a2..6e5d28620 100644 --- a/crates/sync/example/web/src/utils/bindings.ts +++ b/crates/sync/example/web/src/utils/bindings.ts @@ -11,7 +11,8 @@ export type Procedures = { { key: "createDatabase", input: string, result: string } | { key: "file_path.create", input: string, result: FilePath } | { key: "pullOperations", input: string, result: null } | - { key: "removeDatabases", input: string, result: null }, + { key: "removeDatabases", input: string, result: null } | + { key: "testCreate", input: string, result: null }, subscriptions: never }; diff --git a/crates/sync/src/crdt.rs b/crates/sync/src/crdt.rs index acba9976b..7e1f9d6f0 100644 --- a/crates/sync/src/crdt.rs +++ b/crates/sync/src/crdt.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::{collections::BTreeMap, fmt::Debug}; use rspc::Type; use serde::{Deserialize, Serialize}; @@ -23,11 +23,14 @@ pub struct RelationOperation { #[derive(Serialize, Deserialize, Clone, Debug, Type)] pub enum SharedOperationCreateData { + #[serde(rename = "u")] Unique(Map), + #[serde(rename = "a")] Atomic, } #[derive(Serialize, Deserialize, Clone, Debug, Type)] +#[serde(untagged)] pub enum SharedOperationData { Create(SharedOperationCreateData), Update { field: String, value: Value }, @@ -36,15 +39,19 @@ pub enum SharedOperationData { #[derive(Serialize, Deserialize, Clone, Debug, Type)] pub struct SharedOperation { - pub record_id: Uuid, + pub record_id: Value, pub model: String, pub data: SharedOperationData, } #[derive(Serialize, Deserialize, Clone, Debug, Type)] pub enum OwnedOperationData { - Create(Map), - Update(Map), + Create(BTreeMap), + CreateMany { + values: Vec<(Value, BTreeMap)>, + skip_duplicates: bool, + }, + Update(BTreeMap), Delete, } diff --git a/crates/sync/src/db.rs b/crates/sync/src/db.rs index d052c0075..6828fd565 100644 --- a/crates/sync/src/db.rs +++ b/crates/sync/src/db.rs @@ -192,8 +192,11 @@ impl Db { match item.data { OwnedOperationData::Create(data) => { - self.file_paths - .insert(id, from_value(Value::Object(data)).unwrap()); + self.file_paths.insert( + id, + from_value(Value::Object(data.into_iter().collect())) + .unwrap(), + ); } OwnedOperationData::Update(data) => { let obj = self.file_paths.get_mut(&id).unwrap(); @@ -252,11 +255,13 @@ impl Db { #[cfg(test)] mod tests { + use std::collections::BTreeMap; + use super::*; - fn to_map(v: &impl serde::Serialize) -> serde_json::Map { + fn to_map(v: &impl serde::Serialize) -> BTreeMap { match to_value(&v).unwrap() { - Value::Object(m) => m, + Value::Object(m) => m.into_iter().collect(), _ => unreachable!(), } } diff --git a/crates/sync/src/lib.rs b/crates/sync/src/lib.rs index 08939416b..bd8d5c857 100644 --- a/crates/sync/src/lib.rs +++ b/crates/sync/src/lib.rs @@ -1,5 +1,58 @@ mod crdt; -mod db; +// mod db; pub use crdt::*; -pub use db::*; +// pub use db::*; + +use prisma_client_rust::ModelActions; +use serde_value::Value; +use std::collections::BTreeMap; + +pub trait CreateCRDTMutation { + fn operation_from_data( + d: &BTreeMap, + typ: CreateOperationType, + ) -> CRDTOperationType; +} + +pub enum CreateOperationType { + Owned, + SharedUnique, + SharedAtomic, + Relation, +} + +impl CreateCRDTMutation for prisma_client_rust::Create<'_, T> { + fn operation_from_data( + _: &BTreeMap, + typ: CreateOperationType, + ) -> CRDTOperationType { + match typ { + CreateOperationType::Owned => { + todo!() + // let id = serde_json::to_value( + // d.iter() + // .filter(|(field, _)| T::id_fields().iter().any(|f| f == field)) + // .collect::>(), + // ) + // .unwrap(); + + // CRDTOperationType::Owned(OwnedOperation { + // model: T::MODEL.to_string(), + // items: [OwnedOperationItem { + // id, + // data: OwnedOperationData::Create( + // d.clone() + // .into_iter() + // .filter(|(field, _)| T::id_fields().iter().all(|f| f != field)) + // .map(|(k, v)| (k, serde_json::to_value(v).unwrap())) + // .collect(), + // ), + // }] + // .to_vec(), + // }) + } + _ => todo!(), + } + } +} diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index 3ac25baea..e951e5153 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -93,7 +93,7 @@ export type ExplorerContext = { type: "Location" } & Location | { type: "Tag" } export interface ExplorerData { context: ExplorerContext, items: Array } -export type ExplorerItem = { type: "Path" } & { id: number, is_dir: boolean, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, object: Object | null } | { type: "Object" } & { id: number, cas_id: string, integrity_checksum: string | null, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, file_paths: Array } +export type ExplorerItem = { type: "Path" } & FilePathWithObject | { type: "Object" } & ObjectWithFilePaths export interface FileDecryptorJobInit { location_id: number, object_id: number, output_path: string | null, password: string | null, save_to_library: boolean | null } @@ -188,3 +188,7 @@ export interface TagCreateArgs { name: string, color: string } export interface TagUpdateArgs { id: number, name: string | null, color: string | null } export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } + +export interface FilePathWithObject { id: number, is_dir: boolean, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, object: Object | null } + +export interface ObjectWithFilePaths { id: number, cas_id: string, integrity_checksum: string | null, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, file_paths: Array }