diff --git a/Cargo.lock b/Cargo.lock index bb7e87a..cbb8786 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -49,9 +49,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -60,24 +60,24 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -88,15 +88,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -114,16 +114,16 @@ dependencies = [ "rustversion", "serde", "sync_wrapper 1.0.1", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -134,7 +134,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", ] @@ -190,15 +190,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.20" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bcde016d64c21da4be18b655631e5ab6d3107607e71a73a9f53eb48aae23fb" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "shlex", ] @@ -343,7 +343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -440,36 +440,36 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-task", @@ -490,9 +490,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -512,7 +512,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -531,6 +531,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.5.0" @@ -579,9 +585,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -597,9 +603,9 @@ checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -631,9 +637,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -644,7 +650,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -684,12 +689,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -698,6 +703,12 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "itertools" version = "0.13.0" @@ -749,9 +760,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jni-toolbox" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeae2881d819e208fcfceea81eb5a8ca6c131c6fb1605dfe2f3a31dea061ec7c" +checksum = "cce03cf89bc32b81de142a323a71e9903ee88127a0e04bbd7f215ab74ab6b10a" dependencies = [ "jni", "jni-toolbox-macro", @@ -760,20 +771,20 @@ dependencies = [ [[package]] name = "jni-toolbox-macro" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e480850db18f0cc95120e7bf86af772c31b3c0f0dd3d3600682d8bd8399f4ae" +checksum = "609491ce00edcf12946945a514d033bf6e8bfbab02c6a25a46ed8cd4749707da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -796,9 +807,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -935,7 +946,7 @@ checksum = "13e6f40fa1fd8426285688f4a37b56beac69284743d057ee6db352b543f4b621" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -946,9 +957,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "napi" -version = "2.16.10" +version = "2.16.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04409e8c2d61995696e44d2181b79b68c1dd41f7e24a17cde60bbd9f54ddddef" +checksum = "3a84fdaf64da2b2d86b1be5db1b81963353bf00f7bef4b9e2668bbe6f72e8eb3" dependencies = [ "bitflags", "chrono", @@ -979,7 +990,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -994,7 +1005,7 @@ dependencies = [ "quote", "regex", "semver", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1048,18 +1059,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl-probe" @@ -1118,27 +1129,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1155,15 +1166,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "ppv-lite86" @@ -1181,7 +1192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1196,18 +1207,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -1215,9 +1226,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" +checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" dependencies = [ "bytes", "heck", @@ -1230,40 +1241,41 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.77", + "syn 2.0.79", "tempfile", ] [[package]] name = "prost-derive" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "prost-types" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" dependencies = [ "prost", ] [[package]] name = "pyo3" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15ee168e30649f7f234c3d49ef5a7a6cbf5134289bc46c29ff3155fa3221c225" +checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51" dependencies = [ "cfg-if", "indoc", + "inventory", "libc", "memoffset", "once_cell", @@ -1276,9 +1288,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e61cef80755fe9e46bb8a0b8f20752ca7676dcc07a5277d8b7768c6172e529b3" +checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179" dependencies = [ "once_cell", "target-lexicon", @@ -1286,9 +1298,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce096073ec5405f5ee2b8b31f03a68e02aa10d5d4f565eca04acc41931fa1c" +checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d" dependencies = [ "libc", "pyo3-build-config", @@ -1296,27 +1308,27 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2440c6d12bc8f3ae39f1e775266fa5122fd0c8891ce7520fa6048e683ad3de28" +checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "pyo3-macros-backend" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be962f0e06da8f8465729ea2cb71a416d2257dff56cbe40a70d3e62a93ae5d1" +checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce" dependencies = [ "heck", "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1360,18 +1372,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -1381,9 +1393,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1392,9 +1404,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ring" @@ -1447,9 +1459,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", @@ -1462,9 +1474,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -1475,19 +1487,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -1502,9 +1513,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -1523,9 +1534,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -1551,9 +1562,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -1592,7 +1603,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1701,9 +1712,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1730,9 +1741,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -1743,22 +1754,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1795,7 +1806,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1845,16 +1856,16 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "toml_datetime", "winnow", ] [[package]] name = "tonic" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", @@ -1877,7 +1888,7 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -1885,15 +1896,16 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", "proc-macro2", "prost-build", + "prost-types", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1916,6 +1928,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1947,7 +1973,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2080,9 +2106,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -2091,24 +2117,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2116,22 +2142,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "winapi" @@ -2354,7 +2380,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f867898..5c3f4d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ napi = { version = "2.16", features = ["full"], optional = true } napi-derive = { version="2.16", optional = true} # glue (python) -pyo3 = { version = "0.22", features = ["extension-module"], optional = true} +pyo3 = { version = "0.22", features = ["extension-module", "multiple-pymethods"], optional = true} # extra async-trait = { version = "0.1", optional = true } diff --git a/dist/java/src/mp/code/Client.java b/dist/java/src/mp/code/Client.java index 12bd9c3..f7d56f5 100644 --- a/dist/java/src/mp/code/Client.java +++ b/dist/java/src/mp/code/Client.java @@ -34,17 +34,17 @@ public final class Client { */ public static native Client connect(Config config) throws ConnectionException; - private static native User get_user(long self); + private static native User current_user(long self); /** * Gets information about the current user. * @return a {@link User} object representing the user */ - public User getUser() { - return get_user(this.ptr); + public User currentUser() { + return current_user(this.ptr); } - private static native Workspace join_workspace(long self, String workspaceId) throws ConnectionException; + private static native Workspace attach_workspace(long self, String workspaceId) throws ConnectionException; /** * Joins a {@link Workspace} and returns it. @@ -52,8 +52,8 @@ public final class Client { * @return the relevant {@link Workspace} * @throws ConnectionException if an error occurs in communicating with the server */ - public Workspace joinWorkspace(String workspaceId) throws ConnectionException { - return join_workspace(this.ptr, workspaceId); + public Workspace attachWorkspace(String workspaceId) throws ConnectionException { + return attach_workspace(this.ptr, workspaceId); } private static native void create_workspace(long self, String workspaceId) throws ConnectionRemoteException; @@ -91,17 +91,26 @@ public final class Client { invite_to_workspace(this.ptr, workspaceId, user); } - private static native String[] list_workspaces(long self, boolean owned, boolean invited) throws ConnectionRemoteException; + private static native String[] fetch_owned_workspaces(long self) throws ConnectionRemoteException; /** - * Lists available workspaces according to certain filters. - * @param owned if owned workspaces should be included - * @param invited if workspaces the user is invited to should be included + * Lists workspaces owned by the current user. * @return an array of workspace IDs * @throws ConnectionRemoteException if an error occurs in communicating with the server */ - public String[] listWorkspaces(boolean owned, boolean invited) throws ConnectionRemoteException { - return list_workspaces(this.ptr, owned, invited); + public String[] fetchOwnedWorkspaces() throws ConnectionRemoteException { + return fetch_owned_workspaces(this.ptr); + } + + private static native String[] fetch_joined_workspaces(long self) throws ConnectionRemoteException; + + /** + * Lists workspaces the current user has joined. + * @return an array of workspace IDs + * @throws ConnectionRemoteException if an error occurs in communicating with the server + */ + public String[] fetchJoinedWorkspaces() throws ConnectionRemoteException { + return fetch_joined_workspaces(this.ptr); } private static native String[] active_workspaces(long self); diff --git a/dist/java/src/mp/code/Workspace.java b/dist/java/src/mp/code/Workspace.java index da9cce7..116f24a 100644 --- a/dist/java/src/mp/code/Workspace.java +++ b/dist/java/src/mp/code/Workspace.java @@ -1,9 +1,10 @@ package mp.code; import java.util.Optional; -import java.util.UUID; import java.util.function.Consumer; +import lombok.Getter; +import mp.code.data.User; import mp.code.exceptions.ConnectionException; import mp.code.exceptions.ConnectionRemoteException; import mp.code.exceptions.ControllerException; @@ -24,24 +25,24 @@ public final class Workspace { Extensions.CLEANER.register(this, () -> free(ptr)); } - private static native String get_workspace_id(long self); + private static native String id(long self); /** * Gets the unique identifier of the current workspace. * @return the identifier */ - public String getWorkspaceId() { - return get_workspace_id(this.ptr); + public String id() { + return id(this.ptr); } - private static native CursorController get_cursor(long self); + private static native CursorController cursor(long self); /** * Gets the {@link CursorController} for the current workspace. * @return the {@link CursorController} */ - public CursorController getCursor() { - return get_cursor(this.ptr); + public CursorController cursor() { + return cursor(this.ptr); } private static native BufferController get_buffer(long self, String path); @@ -56,17 +57,16 @@ public final class Workspace { return Optional.ofNullable(get_buffer(this.ptr, path)); } - private static native String[] get_file_tree(long self, String filter, boolean strict); + private static native String[] search_buffers(long self, String filter); /** - * Gets the file tree for this workspace, optionally filtering it. - * @param filter applies an optional filter to the outputs - * @param strict whether it should be a strict match (equals) or not (startsWith) + * Searches for buffers matching the filter in this workspace. + * @param filter the filter to apply * @return an array containing file tree as flat paths */ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public String[] getFileTree(Optional filter, boolean strict) { - return get_file_tree(this.ptr, filter.orElse(null), strict); + public String[] searchBuffers(Optional filter) { + return search_buffers(this.ptr, filter.orElse(null)); } private static native String[] active_buffers(long self); @@ -101,7 +101,7 @@ public final class Workspace { create_buffer(this.ptr, path); } - private static native BufferController attach_to_buffer(long self, String path) throws ConnectionException; + private static native BufferController attach_buffer(long self, String path) throws ConnectionException; /** * Attaches to an existing buffer with the given path, if present. @@ -109,52 +109,54 @@ public final class Workspace { * @return the {@link BufferController} associated with that path * @throws ConnectionException if an error occurs in communicating with the server, or if the buffer did not exist */ - public BufferController attachToBuffer(String path) throws ConnectionException { - return attach_to_buffer(ptr, path); + public BufferController attachBuffer(String path) throws ConnectionException { + return attach_buffer(ptr, path); } - private static native boolean detach_from_buffer(long self, String path); + private static native boolean detach_buffer(long self, String path); /** * Detaches from a given buffer. * @param path the path of the buffer to detach from * @return a boolean, true only if there are still dangling references preventing controller from stopping */ - public boolean detachFromBuffer(String path) { - return detach_from_buffer(this.ptr, path); + public boolean detachBuffer(String path) { + return detach_buffer(this.ptr, path); } - private static native void fetch_buffers(long self) throws ConnectionRemoteException; + private static native String[] fetch_buffers(long self) throws ConnectionRemoteException; /** - * Updates the local list of buffers. + * Updates and fetches the local list of buffers. + * @return the updated list * @throws ConnectionRemoteException if an error occurs in communicating with the server */ - public void fetchBuffers() throws ConnectionRemoteException { - fetch_buffers(this.ptr); + public String[] fetchBuffers() throws ConnectionRemoteException { + return fetch_buffers(this.ptr); } - private static native void fetch_users(long self) throws ConnectionRemoteException; + private static native User[] fetch_users(long self) throws ConnectionRemoteException; /** - * Updates the local list of users. + * Updates and fetches the local list of users. + * @return the updated list * @throws ConnectionRemoteException if an error occurs in communicating with the server */ - public void fetchUsers() throws ConnectionRemoteException { - fetch_buffers(this.ptr); + public User[] fetchUsers() throws ConnectionRemoteException { + return fetch_users(this.ptr); } - private static native UUID[] list_buffer_users(long self, String path) throws ConnectionRemoteException; + private static native User[] fetch_buffer_users(long self, String path) throws ConnectionRemoteException; /** - * Lists the user attached to a certain buffer. + * Fetches the users attached to a certain buffer. * The user must be attached to the buffer to perform this operation. * @param path the path of the buffer to search - * @return an array of user {@link UUID UUIDs} + * @return an array of {@link User}s * @throws ConnectionRemoteException if an error occurs in communicating with the server, or the user wasn't attached */ - public UUID[] listBufferUsers(String path) throws ConnectionRemoteException { - return list_buffer_users(this.ptr, path); + public User[] fetchBufferUsers(String path) throws ConnectionRemoteException { + return fetch_buffer_users(this.ptr, path); } private static native void delete_buffer(long self, String path) throws ConnectionRemoteException; @@ -234,7 +236,8 @@ public final class Workspace { * Represents a workspace-wide event. */ public static final class Event { - private final Type type; + /** The type of the event. */ + public final @Getter Type type; private final String argument; Event(Type type, String argument) { @@ -272,9 +275,24 @@ public final class Workspace { } else return Optional.empty(); } - enum Type { + /** + * The type of workspace event. + */ + public enum Type { + /** + * Somebody joined a workspace. + * @see #getUserJoined() to get the name + */ USER_JOIN, + /** + * Somebody left a workspace + * @see #getUserLeft() to get the name + */ USER_LEAVE, + /** + * The filetree was updated. + * @see #getChangedBuffer() to see the buffer that changed + */ FILE_TREE_UPDATED } } diff --git a/dist/java/src/mp/code/data/TextChange.java b/dist/java/src/mp/code/data/TextChange.java index 5d13ea9..6d1879e 100644 --- a/dist/java/src/mp/code/data/TextChange.java +++ b/dist/java/src/mp/code/data/TextChange.java @@ -10,19 +10,18 @@ import lombok.ToString; @ToString @EqualsAndHashCode @RequiredArgsConstructor -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") public class TextChange { /** * The starting position of the change. * If negative, it is clamped to 0. */ - public final long start; + public final long startIdx; /** * The ending position of the change. * If negative, it is clamped to 0. */ - public final long end; + public final long endIdx; /** * The content of the change. @@ -37,7 +36,7 @@ public class TextChange { * @return true if this change represents a deletion */ public boolean isDelete() { - return this.start < this.end; + return this.startIdx < this.endIdx; } /** @@ -64,14 +63,14 @@ public class TextChange { * @return the mutated string */ public String apply(String input) { - long preIndex = Math.min(this.start, input.length()); + long preIndex = Math.min(this.startIdx, input.length()); String pre = ""; try { pre = input.substring(0, (int) preIndex); } catch(IndexOutOfBoundsException ignored) {} String post = ""; try { - post = input.substring((int) this.end); + post = input.substring((int) this.endIdx); } catch(IndexOutOfBoundsException ignored) {} return pre + this.content + post; } diff --git a/dist/lua/annotations.lua b/dist/lua/annotations.lua index 71b5ea3..df02187 100644 --- a/dist/lua/annotations.lua +++ b/dist/lua/annotations.lua @@ -158,16 +158,32 @@ function MaybeBufferUpdatePromise:cancel() end ---invoke callback asynchronously as soon as promise is ready function MaybeBufferUpdatePromise:and_then(cb) end +---@class (exact) UserListPromise : Promise +local UserListPromise = {} +--- block until promise is ready and return value +--- @return User[] +function UserListPromise:await() end +--- cancel promise execution +function UserListPromise:cancel() end +---@param cb fun(x: User[]) callback to invoke +---invoke callback asynchronously as soon as promise is ready +function UserListPromise:and_then(cb) end + -- [[ END ASYNC STUFF ]] ---@class (exact) Client ----@field id string uuid of local user ----@field username string name of local user ----@field active_workspaces string[] array of all currently active workspace names ---the effective local client, handling connecting to codemp server local Client = {} +---@return User +---current logged in user for this client +function Client:current_user() end + +---@return string[] +---array of all currently active workspace names +function Client:active_workspaces() end + ---@return NilPromise ---@async ---@nodiscard @@ -179,7 +195,7 @@ function Client:refresh() end ---@async ---@nodiscard ---join requested workspace if possible and subscribe to event bus -function Client:join_workspace(ws) end +function Client:attach_workspace(ws) end ---@param ws string workspace id to create ---@return NilPromise @@ -207,13 +223,17 @@ function Client:delete_workspace(ws) end ---grant user acccess to workspace function Client:invite_to_workspace(ws, user) end ----@param owned boolean? list owned workspaces, default true ----@param invited boolean? list invited workspaces, default true ---@return StringArrayPromise ---@async ---@nodiscard ----grant user acccess to workspace -function Client:list_workspaces(owned, invited) end +---fetch and list owned workspaces +function Client:fetch_owned_workspaces() end + +---@return StringArrayPromise +---@async +---@nodiscard +---fetch and list joined workspaces +function Client:fetch_joined_workspaces() end ---@param ws string workspace id to get ---@return Workspace? @@ -222,26 +242,41 @@ function Client:get_workspace(ws) end +---@class User +---@field id string user uuid +---@field name string user display name + + + ---@class (exact) Workspace ----@field name string workspace name ----@field cursor CursorController workspace cursor controller ----@field active_buffers string[] array of all currently active buffer names ---a joined codemp workspace local Workspace = {} +---@return string +---workspace id +function Workspace:id() end + +---@return string[] +---array of all currently active buffer names +function Workspace:active_buffers() end + +---@return CursorController +---reference to workspace's CursorController +function Workspace:cursor() end + ---@param path string relative path ("name") of new buffer ---@return NilPromise ---@async ---@nodiscard ---create a new empty buffer -function Workspace:create(path) end +function Workspace:create_buffer(path) end ---@param path string relative path ("name") of buffer to delete ---@return NilPromise ---@async ---@nodiscard ---delete buffer from workspace -function Workspace:delete(path) end +function Workspace:delete_buffer(path) end ---@param path string relative path ("name") of buffer to get ---@return BufferController? @@ -253,20 +288,19 @@ function Workspace:get_buffer(path) end ---@async ---@nodiscard ---attach to a remote buffer, synching content and changes and returning its controller -function Workspace:attach(path) end +function Workspace:attach_buffer(path) end ---@param path string relative path ("name") of buffer to detach from ---@return boolean success ---detach from an active buffer, closing all streams. returns false if there are still dangling references -function Workspace:detach(path) end +function Workspace:detach_buffer(path) end ---@param filter? string apply a filter to the return elements ----@param strict? boolean whether to strictly match or just check whether it starts with it ---@return string[] ---return the list of available buffers in this workspace, as relative paths from workspace root -function Workspace:filetree(filter, strict) end +function Workspace:search_buffers(filter) end ----@return string[] +---@return User[] ---return all names of users currently in this workspace function Workspace:user_list() end @@ -282,6 +316,13 @@ function Workspace:fetch_buffers(path) end ---force refresh users list from workspace function Workspace:fetch_users(path) end +---@param path string the buffer to look in +---@return UserListPromise +---@async +---@nodiscard +---fetch the list of users in the given buffer +function Workspace:fetch_buffer_users(path) end + ---@class (exact) WorkspaceEvent ---@field type string ---@field value string @@ -320,8 +361,8 @@ local BufferController = {} ---@class TextChange ---@field content string text content of change ----@field start integer start index of change ----@field finish integer end index of change +---@field start_idx integer start index of change +---@field end_idx integer end index of change local TextChange = {} ---@class (exact) BufferUpdate diff --git a/dist/py/src/codemp/codemp.pyi b/dist/py/src/codemp/codemp.pyi index 25b1ba8..5b09cb5 100644 --- a/dist/py/src/codemp/codemp.pyi +++ b/dist/py/src/codemp/codemp.pyi @@ -7,6 +7,13 @@ class Driver: """ def stop(self) -> None: ... +class User: + """ + A remote user, with uuid and username + """ + id: str + name: str + class Config: """ Configuration data structure for codemp clients @@ -17,7 +24,7 @@ class Config: port: Optional[int] tls: Optional[bool] - def __new__(cls, *, username: str, password: str, **kwargs) -> Config: ... + def __new__(cls, username: str, password: str, **kwargs) -> Config: ... def init() -> Driver: ... def set_logger(logger_cb: Callable[[str], None], debug: bool) -> bool: ... @@ -32,25 +39,25 @@ class Promise[T]: It can either be used directly or you can wrap it inside a future python side. """ - def wait(self) -> T: ... - def is_done(self) -> bool: ... + def wait(self) -> T: ... + def is_done(self) -> bool: ... class Client: """ Handle to the actual client that manages the session. It manages the connection to a server and joining/creating new workspaces """ - def join_workspace(self, workspace: str) -> Promise[Workspace]: ... - def create_workspace(self, workspace: str) -> Promise[None]: ... - def delete_workspace(self, workspace: str) -> Promise[None]: ... + def attach_workspace(self, workspace: str) -> Promise[Workspace]: ... + def create_workspace(self, workspace: str) -> Promise[None]: ... + def delete_workspace(self, workspace: str) -> Promise[None]: ... def invite_to_workspace(self, workspace: str, username: str) -> Promise[None]: ... - def list_workspaces(self, owned: bool, invited: bool) -> Promise[list[str]]: ... - def leave_workspace(self, workspace: str) -> bool: ... - def get_workspace(self, id: str) -> Workspace: ... - def active_workspaces(self) -> list[str]: ... - def user_id(self) -> str: ... - def user_name(self) -> str: ... - def refresh(self) -> Promise[None]: ... + def fetch_owned_workspaces(self) -> Promise[list[str]]: ... + def fetch_joined_workspaces(self) -> Promise[list[str]]: ... + def leave_workspace(self, workspace: str) -> bool: ... + def get_workspace(self, id: str) -> Workspace: ... + def active_workspaces(self) -> list[str]: ... + def current_user(self) -> User: ... + def refresh(self) -> Promise[None]: ... class Event: pass @@ -60,22 +67,23 @@ class Workspace: Handle to a workspace inside codemp. It manages buffers. A cursor is tied to the single workspace. """ - def create(self, path: str) -> Promise[None]: ... - def attach(self, path: str) -> Promise[BufferController]: ... - def detach(self, path: str) -> bool: ... - def fetch_buffers(self) -> Promise[None]: ... - def fetch_users(self) -> Promise[None]: ... - def list_buffer_users(self, path: str) -> Promise[list[str]]: ... - def delete(self, path: str) -> Promise[None]: ... - def id(self) -> str: ... - def cursor(self) -> CursorController: ... - def buffer_by_name(self, path: str) -> Optional[BufferController]: ... - def buffer_list(self) -> list[str]: ... - def filetree(self, filter: Optional[str], strict: bool) -> list[str]: ... - def recv(self) -> Promise[Event]: ... - def try_recv(self) -> Promise[Optional[Event]]: ... - def poll(self) -> Promise[None]: ... - def clear_callback(self) -> None: ... + def create_buffer(self, path: str) -> Promise[None]: ... + def attach_buffer(self, path: str) -> Promise[BufferController]: ... + def detach_buffer(self, path: str) -> bool: ... + def fetch_buffers(self) -> Promise[list[str]]: ... + def fetch_users(self) -> Promise[list[User]]: ... + def fetch_buffer_users(self, path: str) -> Promise[list[User]]: ... + def delete_buffer(self, path: str) -> Promise[None]: ... + def id(self) -> str: ... + def cursor(self) -> CursorController: ... + def get_buffer(self, path: str) -> Optional[BufferController]: ... + def user_list(self) -> list[User]: ... + def active_buffers(self) -> list[str]: ... + def search_buffers(self, filter: Optional[str]) -> list[str]: ... + def recv(self) -> Promise[Event]: ... + def try_recv(self) -> Promise[Optional[Event]]: ... + def poll(self) -> Promise[None]: ... + def clear_callback(self) -> None: ... def callback(self, cb: Callable[[Workspace], None]) -> None: ... class TextChange: @@ -87,10 +95,10 @@ class TextChange: end: int content: str - def is_delete(self) -> bool: ... - def is_insert(self) -> bool: ... - def is_empty(self) -> bool: ... - def apply(self, txt: str) -> str: ... + def is_delete(self) -> bool: ... + def is_insert(self) -> bool: ... + def is_empty(self) -> bool: ... + def apply(self, txt: str) -> str: ... class BufferUpdate: """ @@ -106,19 +114,16 @@ class BufferController: Handle to the controller for a specific buffer, which manages the back and forth of operations to and from other peers. """ - def path(self) -> str: ... - def content(self) -> Promise[str]: ... - def ack(self, v: list[int]) -> None: ... - def send(self, - start: int, - end: int, - txt: str) -> Promise[None]: ... - def try_recv(self) -> Promise[Optional[TextChange]]: ... - def recv(self) -> Promise[TextChange]: ... - def poll(self) -> Promise[None]: ... + def path(self) -> str: ... + def content(self) -> Promise[str]: ... + def ack(self, v: list[int]) -> None: ... + def send(self, op: TextChange) -> None: ... + def try_recv(self) -> Promise[Optional[TextChange]]: ... + def recv(self) -> Promise[TextChange]: ... + def poll(self) -> Promise[None]: ... def callback(self, cb: Callable[[BufferController], None]) -> None: ... - def clear_callback(self) -> None: ... + def clear_callback(self) -> None: ... @@ -143,14 +148,11 @@ class CursorController: Handle to the controller for a workspace, which manages the back and forth of cursor movements to and from other peers """ - def send(self, - path: str, - start: Tuple[int, int], - end: Tuple[int, int]) -> Promise[None]: ... - def try_recv(self) -> Promise[Optional[Cursor]]: ... - def recv(self) -> Promise[Cursor]: ... - def poll(self) -> Promise[None]: ... + def send(self, pos: Selection) -> None: ... + def try_recv(self) -> Promise[Optional[Cursor]]: ... + def recv(self) -> Promise[Cursor]: ... + def poll(self) -> Promise[None]: ... def callback(self, cb: Callable[[CursorController], None]) -> None: ... - def clear_callback(self) -> None: ... + def clear_callback(self) -> None: ... diff --git a/src/api/change.rs b/src/api/change.rs index 6270e37..60d28f2 100644 --- a/src/api/change.rs +++ b/src/api/change.rs @@ -31,18 +31,18 @@ pub struct BufferUpdate { /// ### Examples /// To insert 'a' after 4th character we should send: /// ``` -/// codemp::api::TextChange { start: 4, end: 4, content: "a".into() }; +/// codemp::api::TextChange { start_idx: 4, end_idx: 4, content: "a".into() }; /// ``` /// /// To delete the fourth character we should send: /// ``` -/// codemp::api::TextChange { start: 3, end: 4, content: "".into() }; +/// codemp::api::TextChange { start_idx: 3, end_idx: 4, content: "".into() }; /// ``` /// /// ``` /// let change = codemp::api::TextChange { -/// start: 6, -/// end: 11, +/// start_idx: 6, +/// end_idx: 11, /// content: "mom".to_string() /// }; /// let before = "hello world!"; @@ -55,10 +55,9 @@ pub struct BufferUpdate { #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct TextChange { /// Range start of text change, as char indexes in buffer previous state. - pub start: u32, + pub start_idx: u32, /// Range end of text change, as char indexes in buffer previous state. - #[cfg_attr(feature = "serialize", serde(alias = "finish"))] // Lua uses `end` as keyword - pub end: u32, + pub end_idx: u32, /// New content of text inside span. pub content: String, } @@ -66,7 +65,7 @@ pub struct TextChange { impl TextChange { /// Returns the [`std::ops::Range`] representing this change's span. pub fn span(&self) -> std::ops::Range { - self.start as usize..self.end as usize + self.start_idx as usize..self.end_idx as usize } } @@ -76,7 +75,7 @@ impl TextChange { /// /// Note that this is is **not** mutually exclusive with [TextChange::is_insert]. pub fn is_delete(&self) -> bool { - self.start < self.end + self.start_idx < self.end_idx } /// Returns true if this [`TextChange`] adds new text. @@ -93,9 +92,9 @@ impl TextChange { /// Applies this text change to given text, returning a new string. pub fn apply(&self, txt: &str) -> String { - let pre_index = std::cmp::min(self.start as usize, txt.len()); + let pre_index = std::cmp::min(self.start_idx as usize, txt.len()); let pre = txt.get(..pre_index).unwrap_or("").to_string(); - let post = txt.get(self.end as usize..).unwrap_or("").to_string(); + let post = txt.get(self.end_idx as usize..).unwrap_or("").to_string(); format!("{}{}{}", pre, self.content, post) } } @@ -105,8 +104,8 @@ mod tests { #[test] fn textchange_apply_works_for_insertions() { let change = super::TextChange { - start: 5, - end: 5, + start_idx: 5, + end_idx: 5, content: " cruel".to_string(), }; let result = change.apply("hello world!"); @@ -116,8 +115,8 @@ mod tests { #[test] fn textchange_apply_works_for_deletions() { let change = super::TextChange { - start: 5, - end: 11, + start_idx: 5, + end_idx: 11, content: "".to_string(), }; let result = change.apply("hello cruel world!"); @@ -127,8 +126,8 @@ mod tests { #[test] fn textchange_apply_works_for_replacements() { let change = super::TextChange { - start: 5, - end: 11, + start_idx: 5, + end_idx: 11, content: " not very pleasant".to_string(), }; let result = change.apply("hello cruel world!"); @@ -138,8 +137,8 @@ mod tests { #[test] fn textchange_apply_never_panics() { let change = super::TextChange { - start: 100, - end: 110, + start_idx: 100, + end_idx: 110, content: "a very long string \n which totally matters".to_string(), }; let result = change.apply("a short text"); @@ -152,8 +151,8 @@ mod tests { #[test] fn empty_textchange_doesnt_alter_buffer() { let change = super::TextChange { - start: 42, - end: 42, + start_idx: 42, + end_idx: 42, content: "".to_string(), }; let result = change.apply("some important text"); diff --git a/src/api/event.rs b/src/api/event.rs index c4c73c5..8754778 100644 --- a/src/api/event.rs +++ b/src/api/event.rs @@ -1,29 +1,32 @@ //! # Event //! Real time notification of changes in a workspace, to either users or buffers. +#![allow(non_upper_case_globals, non_camel_case_types)] // pyo3 fix your shit + use codemp_proto::workspace::workspace_event::Event as WorkspaceEventInner; /// Event in a [crate::Workspace]. #[derive(Debug, Clone)] #[cfg_attr(any(feature = "py", feature = "py-noabi"), pyo3::pyclass)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serialize", serde(tag = "type"))] pub enum Event { /// Fired when the file tree changes. /// Contains the modified buffer path (deleted, created or renamed). - FileTreeUpdated(String), + FileTreeUpdated { path: String }, /// Fired when an user joins the current workspace. - UserJoin(String), + UserJoin { name: String }, /// Fired when an user leaves the current workspace. - UserLeave(String), + UserLeave { name: String }, } impl From for Event { fn from(event: WorkspaceEventInner) -> Self { match event { - WorkspaceEventInner::Join(e) => Self::UserJoin(e.user.name), - WorkspaceEventInner::Leave(e) => Self::UserLeave(e.user.name), - WorkspaceEventInner::Create(e) => Self::FileTreeUpdated(e.path), - WorkspaceEventInner::Delete(e) => Self::FileTreeUpdated(e.path), - WorkspaceEventInner::Rename(e) => Self::FileTreeUpdated(e.after), + WorkspaceEventInner::Join(e) => Self::UserJoin { name: e.user.name }, + WorkspaceEventInner::Leave(e) => Self::UserLeave { name: e.user.name }, + WorkspaceEventInner::Create(e) => Self::FileTreeUpdated { path: e.path }, + WorkspaceEventInner::Delete(e) => Self::FileTreeUpdated { path: e.path }, + WorkspaceEventInner::Rename(e) => Self::FileTreeUpdated { path: e.after }, } } } diff --git a/src/api/mod.rs b/src/api/mod.rs index 158a4d0..9e27c22 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -19,11 +19,9 @@ pub mod event; /// data structure for remote users pub mod user; -pub use change::BufferUpdate; -pub use change::TextChange; +pub use change::{BufferUpdate, TextChange}; pub use config::Config; -pub use controller::Controller; -pub use cursor::Cursor; -pub use cursor::Selection; +pub use controller::{AsyncReceiver, AsyncSender, Controller}; +pub use cursor::{Cursor, Selection}; pub use event::Event; pub use user::User; diff --git a/src/api/user.rs b/src/api/user.rs index 2b610f9..28297fd 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -6,6 +6,7 @@ use uuid::Uuid; /// Represents a service user #[derive(Debug, Clone)] +#[cfg_attr(any(feature = "py", feature = "py-noabi"), pyo3::pyclass)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct User { /// User unique identifier, should never change. diff --git a/src/buffer/worker.rs b/src/buffer/worker.rs index 19b25d7..21a4746 100644 --- a/src/buffer/worker.rs +++ b/src/buffer/worker.rs @@ -160,8 +160,8 @@ impl BufferWorker { async fn handle_editor_change(&mut self, change: TextChange, tx: &mpsc::Sender) { let last_ver = self.oplog.local_version(); // clip to buffer extents - let clip_start = change.start as usize; - let mut clip_end = change.end as usize; + let clip_start = change.start_idx as usize; + let mut clip_end = change.end_idx as usize; let b_len = self.branch.len(); if clip_end > b_len { tracing::warn!("clipping TextChange end span from {clip_end} to {b_len}"); @@ -170,8 +170,11 @@ impl BufferWorker { // in case we have a "replace" span if change.is_delete() { - self.branch - .delete_without_content(&mut self.oplog, self.agent_id, clip_start..clip_end); + self.branch.delete_without_content( + &mut self.oplog, + self.agent_id, + clip_start..clip_end, + ); } if change.is_insert() { @@ -247,7 +250,9 @@ impl BufferWorker { { tracing::warn!( "Insert span ({}, {}) differs from effective content len ({})", - dtop.start(), dtop.end(), dtop.content_as_str().unwrap_or_default().len() + dtop.start(), + dtop.end(), + dtop.content_as_str().unwrap_or_default().len() ); } crate::api::BufferUpdate { @@ -257,8 +262,8 @@ impl BufferWorker { .map(|x| i64::from_ne_bytes(x.to_ne_bytes())) .collect(), // TODO this is wasteful change: crate::api::TextChange { - start: dtop.start() as u32, - end: dtop.start() as u32, + start_idx: dtop.start() as u32, + end_idx: dtop.start() as u32, content: dtop.content_as_str().unwrap_or_default().to_string(), }, } @@ -271,8 +276,8 @@ impl BufferWorker { .map(|x| i64::from_ne_bytes(x.to_ne_bytes())) .collect(), // TODO this is wasteful change: crate::api::TextChange { - start: dtop.start() as u32, - end: dtop.end() as u32, + start_idx: dtop.start() as u32, + end_idx: dtop.end() as u32, content: dtop.content_as_str().unwrap_or_default().to_string(), }, }, diff --git a/src/client.rs b/src/client.rs index 7cba102..17ddcbb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -130,9 +130,18 @@ impl Client { Ok(()) } - /// List all available workspaces, also filtering between those owned and those invited to. - pub async fn list_workspaces(&self, owned: bool, invited: bool) -> RemoteResult> { - let mut workspaces = self + /// Fetch the names of all workspaces owned by the current user. + pub async fn fetch_owned_workspaces(&self) -> RemoteResult> { + self.fetch_workspaces(true).await + } + + /// Fetch the names of all workspaces the current user has joined. + pub async fn fetch_joined_workspaces(&self) -> RemoteResult> { + self.fetch_workspaces(false).await + } + + async fn fetch_workspaces(&self, owned: bool) -> RemoteResult> { + let workspaces = self .0 .session .clone() @@ -140,20 +149,18 @@ impl Client { .await? .into_inner(); - let mut out = Vec::new(); - if owned { - out.append(&mut workspaces.owned) + Ok(workspaces.owned) + } else { + Ok(workspaces.invited) } - if invited { - out.append(&mut workspaces.invited) - } - - Ok(out) } /// Join and return a [`Workspace`]. - pub async fn join_workspace(&self, workspace: impl AsRef) -> ConnectionResult { + pub async fn attach_workspace( + &self, + workspace: impl AsRef, + ) -> ConnectionResult { let token = self .0 .session @@ -203,7 +210,7 @@ impl Client { } /// Get the currently logged in user. - pub fn user(&self) -> &User { + pub fn current_user(&self) -> &User { &self.0.user } } diff --git a/src/ffi/java/buffer.rs b/src/ffi/java/buffer.rs index 5f06f8e..edd7b9a 100644 --- a/src/ffi/java/buffer.rs +++ b/src/ffi/java/buffer.rs @@ -2,10 +2,7 @@ use jni::{objects::JObject, JNIEnv}; use jni_toolbox::jni; use crate::{ - api::{ - controller::{AsyncReceiver, AsyncSender}, - BufferUpdate, TextChange, - }, + api::{AsyncReceiver, AsyncSender, BufferUpdate, TextChange}, errors::ControllerError, }; diff --git a/src/ffi/java/client.rs b/src/ffi/java/client.rs index ff14472..230b26a 100644 --- a/src/ffi/java/client.rs +++ b/src/ffi/java/client.rs @@ -7,21 +7,21 @@ use crate::{ use jni_toolbox::jni; /// Connect using the given credentials to the default server, and return a [Client] to interact with it. -#[jni(package = "mp.code", class = "Client", ptr)] +#[jni(package = "mp.code", class = "Client")] fn connect(config: Config) -> Result { super::tokio().block_on(Client::connect(config)) } /// Gets the current [crate::api::User]. -#[jni(package = "mp.code", class = "Client", ptr)] -fn get_user(client: &mut Client) -> crate::api::User { - client.user().clone() +#[jni(package = "mp.code", class = "Client")] +fn current_user(client: &mut Client) -> crate::api::User { + client.current_user().clone() } /// Join a [Workspace] and return a pointer to it. #[jni(package = "mp.code", class = "Client")] -fn join_workspace(client: &mut Client, workspace: String) -> Result { - super::tokio().block_on(client.join_workspace(workspace)) +fn attach_workspace(client: &mut Client, workspace: String) -> Result { + super::tokio().block_on(client.attach_workspace(workspace)) } /// Create a workspace on server, if allowed to. @@ -46,14 +46,16 @@ fn invite_to_workspace( super::tokio().block_on(client.invite_to_workspace(workspace, user)) } -/// List available workspaces. +/// List owned workspaces. #[jni(package = "mp.code", class = "Client")] -fn list_workspaces( - client: &mut Client, - owned: bool, - invited: bool, -) -> Result, RemoteError> { - super::tokio().block_on(client.list_workspaces(owned, invited)) +fn fetch_owned_workspaces(client: &mut Client) -> Result, RemoteError> { + super::tokio().block_on(client.fetch_owned_workspaces()) +} + +/// List joined workspaces. +#[jni(package = "mp.code", class = "Client")] +fn fetch_joined_workspaces(client: &mut Client) -> Result, RemoteError> { + super::tokio().block_on(client.fetch_joined_workspaces()) } /// List available workspaces. diff --git a/src/ffi/java/cursor.rs b/src/ffi/java/cursor.rs index e08a788..4457ddc 100644 --- a/src/ffi/java/cursor.rs +++ b/src/ffi/java/cursor.rs @@ -1,8 +1,5 @@ use crate::{ - api::{ - controller::{AsyncReceiver, AsyncSender}, - Cursor, Selection, - }, + api::{AsyncReceiver, AsyncSender, Cursor, Selection}, errors::ControllerError, }; use jni::{objects::JObject, JNIEnv}; diff --git a/src/ffi/java/mod.rs b/src/ffi/java/mod.rs index 51bd3a5..80ac028 100644 --- a/src/ffi/java/mod.rs +++ b/src/ffi/java/mod.rs @@ -170,9 +170,9 @@ impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::Event { env: &mut jni::JNIEnv<'j>, ) -> Result, jni::errors::Error> { let (ordinal, arg) = match self { - crate::api::Event::UserJoin(arg) => (0, env.new_string(arg)?), - crate::api::Event::UserLeave(arg) => (1, env.new_string(arg)?), - crate::api::Event::FileTreeUpdated(arg) => (2, env.new_string(arg)?), + crate::api::Event::UserJoin { name: arg } => (0, env.new_string(arg)?), + crate::api::Event::UserLeave { name: arg } => (1, env.new_string(arg)?), + crate::api::Event::FileTreeUpdated { path: arg } => (2, env.new_string(arg)?), }; let type_class = env.find_class("mp/code/Workspace$Event$Type")?; @@ -242,8 +242,8 @@ impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::TextChange { class, "(JJLjava/lang/String;)V", &[ - jni::objects::JValueGen::Long(self.start.into()), - jni::objects::JValueGen::Long(self.end.into()), + jni::objects::JValueGen::Long(self.start_idx.into()), + jni::objects::JValueGen::Long(self.end_idx.into()), jni::objects::JValueGen::Object(&content), ], ) @@ -438,11 +438,11 @@ impl<'j> jni_toolbox::FromJava<'j> for crate::api::TextChange { change: Self::From, ) -> Result { let start = env - .get_field(&change, "start", "J")? + .get_field(&change, "startIdx", "J")? .j()? .clamp(0, u32::MAX.into()) as u32; let end = env - .get_field(&change, "end", "J")? + .get_field(&change, "endIdx", "J")? .j()? .clamp(0, u32::MAX.into()) as u32; @@ -457,8 +457,8 @@ impl<'j> jni_toolbox::FromJava<'j> for crate::api::TextChange { }; Ok(Self { - start, - end, + start_idx: start, + end_idx: end, content, }) } diff --git a/src/ffi/java/workspace.rs b/src/ffi/java/workspace.rs index acdc36f..3548a92 100644 --- a/src/ffi/java/workspace.rs +++ b/src/ffi/java/workspace.rs @@ -1,5 +1,5 @@ use crate::{ - api::controller::AsyncReceiver, + api::{controller::AsyncReceiver, User}, errors::{ConnectionError, ControllerError, RemoteError}, ffi::java::null_check, Workspace, @@ -9,86 +9,86 @@ use jni_toolbox::jni; /// Get the workspace id. #[jni(package = "mp.code", class = "Workspace")] -fn get_workspace_id(workspace: &mut Workspace) -> String { +fn id(workspace: &mut Workspace) -> String { workspace.id() } /// Get a cursor controller by name and returns a pointer to it. #[jni(package = "mp.code", class = "Workspace")] -fn get_cursor(workspace: &mut Workspace) -> crate::cursor::Controller { +fn cursor(workspace: &mut Workspace) -> crate::cursor::Controller { workspace.cursor() } /// Get a buffer controller by name and returns a pointer to it. #[jni(package = "mp.code", class = "Workspace")] fn get_buffer(workspace: &mut Workspace, path: String) -> Option { - workspace.buffer_by_name(&path) + workspace.get_buffer(&path) } -/// Get the filetree. +/// Searches for buffers matching the filter. #[jni(package = "mp.code", class = "Workspace")] -fn get_file_tree(workspace: &mut Workspace, filter: Option, strict: bool) -> Vec { - workspace.filetree(filter.as_deref(), strict) +fn search_buffers(workspace: &mut Workspace, filter: Option) -> Vec { + workspace.search_buffers(filter.as_deref()) } /// Gets a list of the active buffers. #[jni(package = "mp.code", class = "Workspace")] fn active_buffers(workspace: &mut Workspace) -> Vec { - workspace.buffer_list() + workspace.active_buffers() } /// Gets a list of the active buffers. #[jni(package = "mp.code", class = "Workspace")] -fn user_list(workspace: &mut Workspace) -> Vec { +fn user_list(workspace: &mut Workspace) -> Vec { workspace.user_list() } /// Create a new buffer. #[jni(package = "mp.code", class = "Workspace")] fn create_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> { - super::tokio().block_on(workspace.create(&path)) + super::tokio().block_on(workspace.create_buffer(&path)) } -/// Attach to a buffer and return a pointer to its [crate::buffer::Controller]. +/// Attach to a buffer and return a pointer to its [`crate::buffer::Controller`]. #[jni(package = "mp.code", class = "Workspace")] -fn attach_to_buffer( +fn attach_buffer( workspace: &mut Workspace, path: String, ) -> Result { - super::tokio().block_on(workspace.attach(&path)) + super::tokio().block_on(workspace.attach_buffer(&path)) } /// Detach from a buffer. #[jni(package = "mp.code", class = "Workspace")] -fn detach_from_buffer(workspace: &mut Workspace, path: String) -> bool { - workspace.detach(&path) +fn detach_buffer(workspace: &mut Workspace, path: String) -> bool { + workspace.detach_buffer(&path) } /// Update the local buffer list. #[jni(package = "mp.code", class = "Workspace")] -fn fetch_buffers(workspace: &mut Workspace) -> Result<(), RemoteError> { +fn fetch_buffers(workspace: &mut Workspace) -> Result, RemoteError> { super::tokio().block_on(workspace.fetch_buffers()) } /// Update the local user list. #[jni(package = "mp.code", class = "Workspace")] -fn fetch_users(workspace: &mut Workspace) -> Result<(), RemoteError> { +fn fetch_users(workspace: &mut Workspace) -> Result, RemoteError> { super::tokio().block_on(workspace.fetch_users()) } -/// List users attached to a buffer. +/// Fetch users attached to a buffer. #[jni(package = "mp.code", class = "Workspace")] -fn list_buffer_users( +fn fetch_buffer_users( workspace: &mut Workspace, path: String, ) -> Result, RemoteError> { - super::tokio().block_on(workspace.list_buffer_users(&path)) + super::tokio().block_on(workspace.fetch_buffer_users(&path)) } /// Delete a buffer. #[jni(package = "mp.code", class = "Workspace")] fn delete_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> { - super::tokio().block_on(workspace.delete(&path)) + super::tokio().block_on(workspace.delete_buffer(&path)) } /// Block and receive a workspace event. diff --git a/src/ffi/js/buffer.rs b/src/ffi/js/buffer.rs index fc9b961..739659c 100644 --- a/src/ffi/js/buffer.rs +++ b/src/ffi/js/buffer.rs @@ -32,13 +32,13 @@ impl BufferController { } /// Remove registered buffer callback - #[napi(js_name = "clear_callback")] + #[napi(js_name = "clearCallback")] pub fn js_clear_callback(&self) { self.clear_callback(); } /// Get buffer path - #[napi(js_name = "get_path")] + #[napi(js_name = "path")] pub fn js_path(&self) -> &str { self.path() } @@ -50,7 +50,7 @@ impl BufferController { } /// Return next buffer event if present - #[napi(js_name = "try_recv")] + #[napi(js_name = "tryRecv")] pub async fn js_try_recv(&self) -> napi::Result> { Ok(self.try_recv().await?) } diff --git a/src/ffi/js/client.rs b/src/ffi/js/client.rs index 8f63862..b6034f6 100644 --- a/src/ffi/js/client.rs +++ b/src/ffi/js/client.rs @@ -34,29 +34,31 @@ pub async fn connect(config: crate::api::Config) -> napi::Result #[napi] impl Client { - #[napi(js_name = "create_workspace")] + #[napi(js_name = "createWorkspace")] /// create workspace with given id, if able to pub async fn js_create_workspace(&self, workspace: String) -> napi::Result<()> { Ok(self.create_workspace(workspace).await?) } - #[napi(js_name = "delete_workspace")] + #[napi(js_name = "deleteWorkspace")] /// delete workspace with given id, if able to pub async fn js_delete_workspace(&self, workspace: String) -> napi::Result<()> { Ok(self.delete_workspace(workspace).await?) } - #[napi(js_name = "list_workspaces")] - /// list available workspaces - pub async fn js_list_workspaces( - &self, - owned: bool, - invited: bool, - ) -> napi::Result> { - Ok(self.list_workspaces(owned, invited).await?) + #[napi(js_name = "fetchOwnedWorkspaces")] + /// fetch owned workspaces + pub async fn js_fetch_owned_workspaces(&self) -> napi::Result> { + Ok(self.fetch_owned_workspaces().await?) } - #[napi(js_name = "invite_to_workspace")] + #[napi(js_name = "fetchJoinedWorkspaces")] + /// fetch joined workspaces + pub async fn js_fetch_joined_workspaces(&self) -> napi::Result> { + Ok(self.fetch_joined_workspaces().await?) + } + + #[napi(js_name = "inviteToWorkspace")] /// invite user to given workspace, if able to pub async fn js_invite_to_workspace( &self, @@ -66,31 +68,31 @@ impl Client { Ok(self.invite_to_workspace(workspace, user).await?) } - #[napi(js_name = "join_workspace")] + #[napi(js_name = "attachWorkspace")] /// join workspace with given id (will start its cursor controller) - pub async fn js_join_workspace(&self, workspace: String) -> napi::Result { - Ok(self.join_workspace(workspace).await?) + pub async fn js_attach_workspace(&self, workspace: String) -> napi::Result { + Ok(self.attach_workspace(workspace).await?) } - #[napi(js_name = "leave_workspace")] + #[napi(js_name = "leaveWorkspace")] /// leave workspace and disconnect, returns true if workspace was active pub async fn js_leave_workspace(&self, workspace: String) -> bool { self.leave_workspace(&workspace) } - #[napi(js_name = "get_workspace")] + #[napi(js_name = "getWorkspace")] /// get workspace with given id, if it exists pub fn js_get_workspace(&self, workspace: String) -> Option { self.get_workspace(&workspace) } - #[napi(js_name = "user")] + #[napi(js_name = "currentUser")] /// return current sessions's user id - pub fn js_user(&self) -> JsUser { - self.user().clone().into() + pub fn js_current_user(&self) -> JsUser { + self.current_user().clone().into() } - #[napi(js_name = "active_workspaces")] + #[napi(js_name = "activeWorkspaces")] /// get list of all active workspaces pub fn js_active_workspaces(&self) -> Vec { self.active_workspaces() diff --git a/src/ffi/js/cursor.rs b/src/ffi/js/cursor.rs index 4759dcc..eb173ad 100644 --- a/src/ffi/js/cursor.rs +++ b/src/ffi/js/cursor.rs @@ -32,7 +32,7 @@ impl CursorController { } /// Clear the registered callback - #[napi(js_name = "clear_callback")] + #[napi(js_name = "clearCallback")] pub fn js_clear_callback(&self) { self.clear_callback(); } @@ -44,7 +44,7 @@ impl CursorController { } /// Get next cursor event if available without blocking - #[napi(js_name = "try_recv")] + #[napi(js_name = "tryRecv")] pub async fn js_try_recv(&self) -> napi::Result> { Ok(self.try_recv().await?.map(crate::api::Cursor::from)) } diff --git a/src/ffi/js/workspace.rs b/src/ffi/js/workspace.rs index bacde0e..d1ba78d 100644 --- a/src/ffi/js/workspace.rs +++ b/src/ffi/js/workspace.rs @@ -8,6 +8,8 @@ use napi::threadsafe_function::{ }; use napi_derive::napi; +use super::client::JsUser; + #[napi(object, js_name = "Event")] pub struct JsEvent { pub r#type: String, @@ -17,15 +19,15 @@ pub struct JsEvent { impl From for JsEvent { fn from(value: crate::api::Event) -> Self { match value { - crate::api::Event::FileTreeUpdated(value) => Self { + crate::api::Event::FileTreeUpdated { path: value } => Self { r#type: "filetree".into(), value, }, - crate::api::Event::UserJoin(value) => Self { + crate::api::Event::UserJoin { name: value } => Self { r#type: "join".into(), value, }, - crate::api::Event::UserLeave(value) => Self { + crate::api::Event::UserLeave { name: value } => Self { r#type: "leave".into(), value, }, @@ -42,21 +44,21 @@ impl Workspace { } /// List all available buffers in this workspace - #[napi(js_name = "filetree")] - pub fn js_filetree(&self, filter: Option<&str>, strict: bool) -> Vec { - self.filetree(filter, strict) + #[napi(js_name = "searchBuffers")] + pub fn js_search_buffers(&self, filter: Option<&str>) -> Vec { + self.search_buffers(filter) } /// List all user names currently in this workspace - #[napi(js_name = "user_list")] - pub fn js_user_list(&self) -> Vec { - self.user_list() + #[napi(js_name = "userList")] + pub fn js_user_list(&self) -> Vec { + self.user_list().into_iter().map(JsUser::from).collect() } /// List all currently active buffers - #[napi(js_name = "buffer_list")] - pub fn js_buffer_list(&self) -> Vec { - self.buffer_list() + #[napi(js_name = "activeBuffers")] + pub fn js_active_buffers(&self) -> Vec { + self.active_buffers() } /// Get workspace's Cursor Controller @@ -66,27 +68,27 @@ impl Workspace { } /// Get a buffer controller by its name (path) - #[napi(js_name = "buffer_by_name")] - pub fn js_buffer_by_name(&self, path: String) -> Option { - self.buffer_by_name(&path) + #[napi(js_name = "getBuffer")] + pub fn js_get_buffer(&self, path: String) -> Option { + self.get_buffer(&path) } /// Create a new buffer in the current workspace - #[napi(js_name = "create")] - pub async fn js_create(&self, path: String) -> napi::Result<()> { - Ok(self.create(&path).await?) + #[napi(js_name = "createBuffer")] + pub async fn js_create_buffer(&self, path: String) -> napi::Result<()> { + Ok(self.create_buffer(&path).await?) } /// Attach to a workspace buffer, starting a BufferController - #[napi(js_name = "attach")] - pub async fn js_attach(&self, path: String) -> napi::Result { - Ok(self.attach(&path).await?) + #[napi(js_name = "attachBuffer")] + pub async fn js_attach_buffer(&self, path: String) -> napi::Result { + Ok(self.attach_buffer(&path).await?) } /// Delete a buffer from workspace - #[napi(js_name = "delete")] - pub async fn js_delete(&self, path: String) -> napi::Result<()> { - Ok(self.delete(&path).await?) + #[napi(js_name = "deleteBuffer")] + pub async fn js_delete_buffer(&self, path: String) -> napi::Result<()> { + Ok(self.delete_buffer(&path).await?) } #[napi(js_name = "recv")] @@ -94,7 +96,7 @@ impl Workspace { Ok(JsEvent::from(self.recv().await?)) } - #[napi(js_name = "try_recv")] + #[napi(js_name = "tryRecv")] pub async fn js_try_recv(&self) -> napi::Result> { Ok(self.try_recv().await?.map(JsEvent::from)) } @@ -105,7 +107,7 @@ impl Workspace { Ok(()) } - #[napi(js_name = "clear_callback")] + #[napi(js_name = "clearCallback")] pub fn js_clear_callback(&self) -> napi::Result<()> { self.clear_callback(); Ok(()) @@ -119,7 +121,7 @@ impl Workspace { })?; self.callback(move |controller: Workspace| { tsfn.call(controller.clone(), ThreadsafeFunctionCallMode::Blocking); //check this with tracing also we could use Ok(event) to get the error - // If it blocks the main thread too many time we have to change this + // If it blocks the main thread too many time we have to change this }); Ok(()) @@ -128,30 +130,35 @@ impl Workspace { /// Detach from an active buffer, stopping its underlying worker /// this method returns true if no reference or last reference was held, false if there are still /// dangling references to clear - #[napi(js_name = "detach")] - pub async fn js_detach(&self, path: String) -> bool { - self.detach(&path) + #[napi(js_name = "detachBuffer")] + pub async fn js_detach_buffer(&self, path: String) -> bool { + self.detach_buffer(&path) } /// Re-fetch remote buffer list - #[napi(js_name = "fetch_buffers")] - pub async fn js_fetch_buffers(&self) -> napi::Result<()> { + #[napi(js_name = "fetchBuffers")] + pub async fn js_fetch_buffers(&self) -> napi::Result> { Ok(self.fetch_buffers().await?) } /// Re-fetch the list of all users in the workspace. - #[napi(js_name = "fetch_users")] - pub async fn js_fetch_users(&self) -> napi::Result<()> { - Ok(self.fetch_users().await?) + #[napi(js_name = "fetchUsers")] + pub async fn js_fetch_users(&self) -> napi::Result> { + Ok(self + .fetch_users() + .await? + .into_iter() + .map(JsUser::from) + .collect()) } /// List users attached to a specific buffer - #[napi(js_name = "list_buffer_users")] - pub async fn js_list_buffer_users( + #[napi(js_name = "fetchBufferUsers")] + pub async fn js_fetch_buffer_users( &self, path: String, ) -> napi::Result> { Ok(self - .list_buffer_users(&path) + .fetch_buffer_users(&path) .await? .into_iter() .map(super::client::JsUser::from) diff --git a/src/ffi/lua/buffer.rs b/src/ffi/lua/buffer.rs index 5bc0813..2392734 100644 --- a/src/ffi/lua/buffer.rs +++ b/src/ffi/lua/buffer.rs @@ -3,7 +3,8 @@ use mlua::prelude::*; use mlua_codemp_patch as mlua; use super::ext::a_sync::a_sync; -use super::ext::from_lua_serde; + +super::ext::impl_lua_serde! { CodempTextChange CodempBufferUpdate } impl LuaUserData for CodempBufferController { fn add_methods>(methods: &mut M) { @@ -38,36 +39,3 @@ impl LuaUserData for CodempBufferController { }); } } - -from_lua_serde! { CodempTextChange } -impl LuaUserData for CodempTextChange { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("content", |_, this| Ok(this.content.clone())); - fields.add_field_method_get("start", |_, this| Ok(this.start)); - fields.add_field_method_get("end", |_, this| Ok(this.end)); - // add a 'finish' accessor too because in Lua 'end' is reserved - fields.add_field_method_get("finish", |_, this| Ok(this.end)); - } - - fn add_methods>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { - Ok(format!("{:?}", this)) - }); - methods.add_method("apply", |_, this, (txt,): (String,)| Ok(this.apply(&txt))); - } -} - -from_lua_serde! { CodempBufferUpdate } -impl LuaUserData for CodempBufferUpdate { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("hash", |_, this| Ok(this.hash)); - fields.add_field_method_get("version", |_, this| Ok(this.version.clone())); - fields.add_field_method_get("change", |_, this| Ok(this.change.clone())); - } - - fn add_methods>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { - Ok(format!("{:?}", this)) - }); - } -} diff --git a/src/ffi/lua/client.rs b/src/ffi/lua/client.rs index 6198671..4a8f4d2 100644 --- a/src/ffi/lua/client.rs +++ b/src/ffi/lua/client.rs @@ -3,28 +3,30 @@ use mlua::prelude::*; use mlua_codemp_patch as mlua; use super::ext::a_sync::a_sync; -use super::ext::from_lua_serde; + +super::ext::impl_lua_serde! { CodempConfig CodempUser } impl LuaUserData for CodempClient { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, this| Ok(this.user().id.to_string())); - fields.add_field_method_get("username", |_, this| Ok(this.user().name.clone())); - fields.add_field_method_get("active_workspaces", |_, this| Ok(this.active_workspaces())); - } - fn add_methods>(methods: &mut M) { methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { Ok(format!("{:?}", this)) }); + methods.add_method("current_user", |_, this, ()| { + Ok(this.current_user().clone()) + }); + methods.add_method("active_workspaces", |_, this, ()| { + Ok(this.active_workspaces()) + }); + methods.add_method( "refresh", |_, this, ()| a_sync! { this => this.refresh().await? }, ); methods.add_method( - "join_workspace", - |_, this, (ws,): (String,)| a_sync! { this => this.join_workspace(ws).await? }, + "attach_workspace", + |_, this, (ws,): (String,)| a_sync! { this => this.attach_workspace(ws).await? }, ); methods.add_method( @@ -41,8 +43,14 @@ impl LuaUserData for CodempClient { a_sync! { this => this.invite_to_workspace(ws, user).await? } ); - methods.add_method("list_workspaces", |_, this, (owned,invited):(Option,Option)| - a_sync! { this => this.list_workspaces(owned.unwrap_or(true), invited.unwrap_or(true)).await? } + methods.add_method( + "fetch_owned_workspaces", + |_, this, ()| a_sync! { this => this.fetch_owned_workspaces().await? }, + ); + + methods.add_method( + "fetch_joined_workspaces", + |_, this, ()| a_sync! { this => this.fetch_joined_workspaces().await? }, ); methods.add_method("leave_workspace", |_, this, (ws,): (String,)| { @@ -54,14 +62,3 @@ impl LuaUserData for CodempClient { }); } } - -from_lua_serde! { CodempConfig } -impl LuaUserData for CodempConfig { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("username", |_, this| Ok(this.username.clone())); - fields.add_field_method_get("password", |_, this| Ok(this.password.clone())); - fields.add_field_method_get("host", |_, this| Ok(this.host.clone())); - fields.add_field_method_get("port", |_, this| Ok(this.port)); - fields.add_field_method_get("tls", |_, this| Ok(this.tls)); - } -} diff --git a/src/ffi/lua/cursor.rs b/src/ffi/lua/cursor.rs index 86a3a33..5a7c220 100644 --- a/src/ffi/lua/cursor.rs +++ b/src/ffi/lua/cursor.rs @@ -3,7 +3,8 @@ use mlua::prelude::*; use mlua_codemp_patch as mlua; use super::ext::a_sync::a_sync; -use super::ext::from_lua_serde; + +super::ext::impl_lua_serde! { CodempCursor CodempSelection } impl LuaUserData for CodempCursorController { fn add_methods>(methods: &mut M) { @@ -29,34 +30,3 @@ impl LuaUserData for CodempCursorController { }); } } - -from_lua_serde! { CodempCursor } -impl LuaUserData for CodempCursor { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("user", |_, this| Ok(this.user.clone())); - fields.add_field_method_get("sel", |_, this| Ok(this.sel.clone())); - } - - fn add_methods>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { - Ok(format!("{:?}", this)) - }); - } -} - -from_lua_serde! { CodempSelection } -impl LuaUserData for CodempSelection { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("buffer", |_, this| Ok(this.buffer.clone())); - fields.add_field_method_get("start_row", |_, this| Ok(this.start_row)); - fields.add_field_method_get("start_col", |_, this| Ok(this.start_col)); - fields.add_field_method_get("end_row", |_, this| Ok(this.end_row)); - fields.add_field_method_get("end_col", |_, this| Ok(this.end_col)); - } - - fn add_methods>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { - Ok(format!("{:?}", this)) - }); - } -} diff --git a/src/ffi/lua/ext/callback.rs b/src/ffi/lua/ext/callback.rs index 7c5f360..54d8e03 100644 --- a/src/ffi/lua/ext/callback.rs +++ b/src/ffi/lua/ext/callback.rs @@ -101,6 +101,7 @@ macro_rules! callback_args { callback_args! { Str: String, VecStr: Vec, + VecUser: Vec, Client: CodempClient, CursorController: CodempCursorController, BufferController: CodempBufferController, diff --git a/src/ffi/lua/ext/mod.rs b/src/ffi/lua/ext/mod.rs index 1cb07c6..a209c0a 100644 --- a/src/ffi/lua/ext/mod.rs +++ b/src/ffi/lua/ext/mod.rs @@ -5,7 +5,7 @@ pub mod log; pub(crate) use a_sync::tokio; pub(crate) use callback::callback; -macro_rules! from_lua_serde { +macro_rules! impl_lua_serde { ($($t:ty)*) => { $( impl FromLua for $t { @@ -13,8 +13,14 @@ macro_rules! from_lua_serde { lua.from_value(value) } } + + impl IntoLua for $t { + fn into_lua(self, lua: &Lua) -> LuaResult { + lua.to_value(&self) + } + } )* }; } -pub(crate) use from_lua_serde; +pub(crate) use impl_lua_serde; diff --git a/src/ffi/lua/workspace.rs b/src/ffi/lua/workspace.rs index 3be3ec3..2094f75 100644 --- a/src/ffi/lua/workspace.rs +++ b/src/ffi/lua/workspace.rs @@ -3,7 +3,8 @@ use mlua::prelude::*; use mlua_codemp_patch as mlua; use super::ext::a_sync::a_sync; -use super::ext::from_lua_serde; + +super::ext::impl_lua_serde! { CodempEvent } impl LuaUserData for CodempWorkspace { fn add_methods>(methods: &mut M) { @@ -11,26 +12,26 @@ impl LuaUserData for CodempWorkspace { Ok(format!("{:?}", this)) }); methods.add_method( - "create", - |_, this, (name,): (String,)| a_sync! { this => this.create(&name).await? }, + "create_buffer", + |_, this, (name,): (String,)| a_sync! { this => this.create_buffer(&name).await? }, ); methods.add_method( - "attach", - |_, this, (name,): (String,)| a_sync! { this => this.attach(&name).await? }, + "attach_buffer", + |_, this, (name,): (String,)| a_sync! { this => this.attach_buffer(&name).await? }, ); - methods.add_method("detach", |_, this, (name,): (String,)| { - Ok(this.detach(&name)) + methods.add_method("detach_buffer", |_, this, (name,): (String,)| { + Ok(this.detach_buffer(&name)) }); methods.add_method( - "delete", - |_, this, (name,): (String,)| a_sync! { this => this.delete(&name).await? }, + "delete_buffer", + |_, this, (name,): (String,)| a_sync! { this => this.delete_buffer(&name).await? }, ); methods.add_method("get_buffer", |_, this, (name,): (String,)| { - Ok(this.buffer_by_name(&name)) + Ok(this.get_buffer(&name)) }); methods.add_method( @@ -42,13 +43,19 @@ impl LuaUserData for CodempWorkspace { |_, this, ()| a_sync! { this => this.fetch_users().await? }, ); - methods.add_method( - "filetree", - |_, this, (filter, strict): (Option, Option)| { - Ok(this.filetree(filter.as_deref(), strict.unwrap_or(false))) - }, - ); + methods.add_method("search_buffers", |_, this, (filter,): (Option,)| { + Ok(this.search_buffers(filter.as_deref())) + }); + methods.add_method("fetch_buffer_users", |_, this, (path,): (String,)| { + a_sync! { + this => this.fetch_buffer_users(&path).await? + } + }); + + methods.add_method("id", |_, this, ()| Ok(this.id())); + methods.add_method("cursor", |_, this, ()| Ok(this.cursor())); + methods.add_method("active_buffers", |_, this, ()| Ok(this.active_buffers())); methods.add_method("user_list", |_, this, ()| Ok(this.user_list())); methods.add_method("recv", |_, this, ()| a_sync! { this => this.recv().await? }); @@ -68,33 +75,4 @@ impl LuaUserData for CodempWorkspace { methods.add_method("clear_callback", |_, this, ()| Ok(this.clear_callback())); } - - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("name", |_, this| Ok(this.id())); - fields.add_field_method_get("cursor", |_, this| Ok(this.cursor())); - fields.add_field_method_get("active_buffers", |_, this| Ok(this.buffer_list())); - // fields.add_field_method_get("users", |_, this| Ok(this.0.users())); // TODO - } -} - -from_lua_serde! { CodempEvent } -impl LuaUserData for CodempEvent { - fn add_methods>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { - Ok(format!("{:?}", this)) - }); - } - - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("type", |_, this| match this { - CodempEvent::FileTreeUpdated(_) => Ok("filetree"), - CodempEvent::UserJoin(_) => Ok("join"), - CodempEvent::UserLeave(_) => Ok("leave"), - }); - fields.add_field_method_get("value", |_, this| match this { - CodempEvent::FileTreeUpdated(x) - | CodempEvent::UserJoin(x) - | CodempEvent::UserLeave(x) => Ok(x.clone()), - }); - } } diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 1ddd89a..2fd6fb1 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -5,26 +5,26 @@ //! ```no_run //! # async { //! use codemp::api::controller::{AsyncReceiver, AsyncSender}; // needed for send/recv trait methods -//! +//! //! // connect first, api.code.mp is managed by hexed.technology //! let client = codemp::Client::connect(codemp::api::Config::new( //! "mail@example.net", "dont-use-this-password" //! )).await?; -//! +//! //! // create and join a workspace //! client.create_workspace("some-workspace").await?; -//! let workspace = client.join_workspace("some-workspace").await?; -//! +//! let workspace = client.attach_workspace("some-workspace").await?; +//! //! // create a new buffer in this workspace and attach to it -//! workspace.create("/my/file.txt").await?; -//! let buffer = workspace.attach("/my/file.txt").await?; -//! +//! workspace.create_buffer("/my/file.txt").await?; +//! let buffer = workspace.attach_buffer("/my/file.txt").await?; +//! //! // write `hello!` at the beginning of this buffer //! buffer.send(codemp::api::TextChange { -//! start: 0, end: 0, +//! start_idx: 0, end_idx: 0, //! content: "hello!".to_string(), //! })?; -//! +//! //! // wait for cursor movements //! loop { //! let event = workspace.cursor().recv().await?; @@ -42,26 +42,26 @@ //! //! ```js //! import * as codemp from 'codemp'; -//! +//! //! // connect first, api.code.mp is managed by hexed.technology //! let client = await codemp.connect({ //! username: "mail@example.net", password: "dont-use-this-password" //! }); -//! +//! //! // create and join a workspace -//! await client.create_workspace("some-workspace"); -//! let workspace = await client.join_workspace("some-workspace"); -//! +//! await client.createWorkspace("some-workspace"); +//! let workspace = await client.attachWorkspace("some-workspace"); +//! //! // create a new buffer in this workspace and attach to it -//! await workspace.create("/my/file.txt"); -//! let buffer = await workspace.attach("/my/file.txt"); -//! +//! await workspace.createBuffer("/my/file.txt"); +//! let buffer = await workspace.attachBuffer("/my/file.txt"); +//! //! // write `hello!` at the beginning of this buffer //! await buffer.send({ -//! start: 0, end: 0, +//! start_idx: 0, end_idx: 0, //! content: "hello!", //! }); -//! +//! //! // wait for cursor movements //! while (true) { //! let event = await workspace.cursor().recv(); @@ -78,30 +78,31 @@ //! //! ```py //! import codemp -//! +//! //! # connect first, api.code.mp is managed by hexed.technology -//! config = codemp.get_default_config() -//! config.username = "mail@example.net" -//! config.password = "dont-use-this-password" -//! client = codemp.connect(config).wait() -//! +//! client = codemp.connect( +//! codemp.Config('mail@example.net', 'dont-use-this-password') +//! ).wait() +//! //! # create and join a workspace //! client.create_workspace("some-workspace").wait() -//! workspace = client.join_workspace("some-workspace").wait() -//! +//! workspace = client.attach_workspace("some-workspace").wait() +//! //! # create a new buffer in this workspace and attach to it //! workspace.create("/my/file.txt").wait() -//! buffer = workspace.attach("/my/file.txt").wait() -//! +//! buffer = workspace.attach_buffer("/my/file.txt").wait() +//! //! # write `hello!` at the beginning of this buffer -//! buffer.send( -//! 0, 0, "hello!" -//! ).wait() -//! +//! buffer.send(codemp.TextChange( +//! start_idx=0, end_idx=0, +//! content="hello!" +//! )).wait() +//! //! # wait for cursor movements //! while true: //! event = workspace.cursor().recv().wait() //! print(f"user {event.user} moved on buffer {event.buffer}") +//! //! ``` //! //! ## Lua @@ -124,26 +125,26 @@ //! //! ```lua //! CODEMP = require('codemp') -//! +//! //! -- connect first, api.code.mp is managed by hexed.technology //! local client = CODEMP.connect({ //! username = "mail@example.net", password = "dont-use-this-password" //! }):await() -//! +//! //! -- create and join a workspace //! client:create_workspace("my-workspace"):await() -//! local workspace = client:join_workspace("my-workspace"):await() -//! +//! local workspace = client:attach_workspace("my-workspace"):await() +//! //! -- create a new buffer in this workspace and attach to it //! workspace:create_buffer("/my/file.txt"):await() //! local buffer = workspace:attach_buffer("/my/file.txt"):await() -//! +//! //! -- write `hello!` at the beginning of this buffer //! buffer:send({ -//! start = 0, finish = 0, +//! start_idx = 0, end_idx = 0, //! content = "hello!" //! }):await() -//! +//! //! -- wait for cursor movements //! while true do //! local event = workspace.cursor:recv():await() @@ -162,29 +163,29 @@ //! //! ```java //! import mp.code.*; -//! +//! //! // connect first, api.code.mp is managed by hexed.technology //! Client client = Client.connect( //! new data.Config("mail@example.net", "dont-use-this-password") //! ); -//! +//! //! // create and join a workspace //! client.createWorkspace("some-workspace"); -//! Workspace workspace = client.joinWorkspace("some-workspace"); -//! +//! Workspace workspace = client.attachWorkspace("some-workspace"); +//! //! // create a new buffer in this workspace and attach to it //! workspace.createBuffer("/my/file.txt"); -//! BufferController buffer = workspace.attachToBuffer("/my/file.txt"); -//! +//! BufferController buffer = workspace.attachBuffer("/my/file.txt"); +//! //! // write `hello!` at the beginning of this buffer //! buffer.send(new data.TextChange( //! 0, 0, "hello!", //! java.util.OptionalLong.empty() // optional, used for error detection //! )); -//! +//! //! // wait for cursor movements //! while (true) { -//! data.Cursor event = workspace.getCursor().recv(); +//! data.Cursor event = workspace.cursor().recv(); //! System.out.printf("user %s moved on buffer %s\n", event.user, event.buffer); //! } //! ``` diff --git a/src/ffi/python/client.rs b/src/ffi/python/client.rs index ffeb3a6..46bc033 100644 --- a/src/ffi/python/client.rs +++ b/src/ffi/python/client.rs @@ -1,5 +1,6 @@ use super::a_sync_allow_threads; use super::Client; +use crate::api::User; use crate::workspace::Workspace; use pyo3::prelude::*; @@ -14,11 +15,11 @@ impl Client { // super::tokio().block_on(Client::connect(host, username, password)) // } - #[pyo3(name = "join_workspace")] - fn pyjoin_workspace(&self, py: Python<'_>, workspace: String) -> PyResult { + #[pyo3(name = "attach_workspace")] + fn pyattach_workspace(&self, py: Python<'_>, workspace: String) -> PyResult { tracing::info!("attempting to join the workspace {}", workspace); let this = self.clone(); - a_sync_allow_threads!(py, this.join_workspace(workspace).await) + a_sync_allow_threads!(py, this.attach_workspace(workspace).await) // let this = self.clone(); // Ok(super::Promise(Some(tokio().spawn(async move { // Ok(this @@ -54,16 +55,18 @@ impl Client { a_sync_allow_threads!(py, this.invite_to_workspace(workspace, user).await) } - #[pyo3(name = "list_workspaces")] - fn pylist_workspaces( - &self, - py: Python<'_>, - owned: bool, - invited: bool, - ) -> PyResult { - tracing::info!("attempting to list workspaces"); + #[pyo3(name = "fetch_owned_workspaces")] + fn pyfetch_owned_workspaces(&self, py: Python<'_>) -> PyResult { + tracing::info!("attempting to fetch owned workspaces"); let this = self.clone(); - a_sync_allow_threads!(py, this.list_workspaces(owned, invited).await) + a_sync_allow_threads!(py, this.fetch_owned_workspaces().await) + } + + #[pyo3(name = "fetch_joined_workspaces")] + fn pyfetch_joined_workspaces(&self, py: Python<'_>) -> PyResult { + tracing::info!("attempting to fetch joined workspaces"); + let this = self.clone(); + a_sync_allow_threads!(py, this.fetch_joined_workspaces().await) } #[pyo3(name = "leave_workspace")] @@ -82,14 +85,9 @@ impl Client { self.active_workspaces() } - #[pyo3(name = "user_id")] - fn pyuser_id(&self) -> String { - self.user().id.to_string() - } - - #[pyo3(name = "user_name")] - fn pyuser_name(&self) -> String { - self.user().name.clone() + #[pyo3(name = "current_user")] + fn pycurrent_user(&self) -> User { + self.current_user().clone() } #[pyo3(name = "refresh")] diff --git a/src/ffi/python/controllers.rs b/src/ffi/python/controllers.rs index 9d9d5f3..01b2cc5 100644 --- a/src/ffi/python/controllers.rs +++ b/src/ffi/python/controllers.rs @@ -13,22 +13,8 @@ use super::Promise; #[pymethods] impl CursorController { #[pyo3(name = "send")] - fn pysend( - &self, - _py: Python, - path: String, - start: (i32, i32), - end: (i32, i32), - ) -> PyResult<()> { - let pos = Selection { - start_row: start.0, - start_col: start.1, - end_row: end.0, - end_col: end.1, - buffer: path, - }; - let this = self.clone(); - this.send(pos)?; + fn pysend(&self, _py: Python, pos: Selection) -> PyResult<()> { + self.send(pos)?; Ok(()) } @@ -86,12 +72,7 @@ impl BufferController { } #[pyo3(name = "send")] - fn pysend(&self, _py: Python, start: u32, end: u32, txt: String) -> PyResult<()> { - let op = TextChange { - start, - end, - content: txt, - }; + fn pysend(&self, _py: Python, op: TextChange) -> PyResult<()> { let this = self.clone(); this.send(op)?; Ok(()) diff --git a/src/ffi/python/mod.rs b/src/ffi/python/mod.rs index bbeb91b..a0f7b8e 100644 --- a/src/ffi/python/mod.rs +++ b/src/ffi/python/mod.rs @@ -3,15 +3,15 @@ pub mod controllers; pub mod workspace; use crate::{ - api::{Config, Cursor, TextChange}, + api::{BufferUpdate, Config, Cursor, Selection, TextChange, User}, buffer::Controller as BufferController, cursor::Controller as CursorController, Client, Workspace, }; -use pyo3::prelude::*; use pyo3::{ exceptions::{PyConnectionError, PyRuntimeError, PySystemError}, + prelude::*, types::PyDict, }; @@ -153,13 +153,37 @@ fn init() -> PyResult { Ok(Driver(Some(rt_stop_tx))) } -#[pyfunction] -fn get_default_config() -> crate::api::Config { - let mut conf = crate::api::Config::new("".to_string(), "".to_string()); - conf.host = Some(conf.host().to_string()); - conf.port = Some(conf.port()); - conf.tls = Some(false); - conf +#[pymethods] +impl User { + #[getter] + fn get_id(&self) -> pyo3::PyResult { + Ok(self.id.to_string()) + } + + #[setter] + fn set_id(&mut self, value: String) -> pyo3::PyResult<()> { + self.id = value + .parse() + .map_err(|x: ::Err| { + pyo3::exceptions::PyRuntimeError::new_err(x.to_string()) + })?; + Ok(()) + } + + #[getter] + fn get_name(&self) -> pyo3::PyResult { + Ok(self.name.clone()) + } + + #[setter] + fn set_name(&mut self, value: String) -> pyo3::PyResult<()> { + self.name = value; + Ok(()) + } + + fn __str__(&self) -> String { + format!("{self:?}") + } } #[pymethods] @@ -176,7 +200,7 @@ impl Config { let port = kwgs.get_item("port")?.and_then(|e| e.extract().ok()); let tls = kwgs.get_item("tls")?.and_then(|e| e.extract().ok()); - Ok(Config { + Ok(Self { username, password, host, @@ -184,9 +208,119 @@ impl Config { tls, }) } else { - Ok(Config::new(username, password)) + Ok(Self::new(username, password)) } } + + fn __str__(&self) -> String { + format!("{self:?}") + } +} + +#[pymethods] +impl Cursor { + fn __str__(&self) -> String { + format!("{self:?}") + } +} + +#[pymethods] +impl Selection { + #[new] + #[pyo3(signature = (**kwds))] + pub fn py_new(kwds: Option<&Bound<'_, PyDict>>) -> PyResult { + if let Some(kwds) = kwds { + let start_row = if let Some(e) = kwds.get_item("start_row")? { + e.extract()? + } else { + 0 + }; + + let start_col = if let Some(e) = kwds.get_item("start_col")? { + e.extract()? + } else { + 0 + }; + + let end_row = if let Some(e) = kwds.get_item("end_row")? { + e.extract()? + } else { + 0 + }; + + let end_col = if let Some(e) = kwds.get_item("end_col")? { + e.extract()? + } else { + 0 + }; + + let buffer = if let Some(e) = kwds.get_item("buffer")? { + e.extract()? + } else { + String::default() + }; + + Ok(Self { + start_row, + start_col, + end_row, + end_col, + buffer, + }) + } else { + Ok(Self::default()) + } + } + + fn __str__(&self) -> String { + format!("{self:?}") + } +} + +#[pymethods] +impl BufferUpdate { + fn __str__(&self) -> String { + format!("{self:?}") + } +} + +#[pymethods] +impl TextChange { + #[new] + #[pyo3(signature = (**kwds))] + pub fn py_new(kwds: Option<&Bound<'_, PyDict>>) -> PyResult { + if let Some(kwds) = kwds { + let start_idx = if let Some(e) = kwds.get_item("start")? { + e.extract()? + } else { + 0 + }; + + let end_idx = if let Some(e) = kwds.get_item("end")? { + e.extract()? + } else { + 0 + }; + + let content = if let Some(e) = kwds.get_item("content")? { + e.extract()? + } else { + String::default() + }; + + Ok(Self { + start_idx, + end_idx, + content, + }) + } else { + Ok(Self::default()) + } + } + + fn __str__(&self) -> String { + format!("{self:?}") + } } #[pyfunction] @@ -254,27 +388,24 @@ impl From for PyErr { } } -impl IntoPy for crate::api::User { - fn into_py(self, py: Python<'_>) -> PyObject { - self.id.to_string().into_py(py) - } -} - #[pymodule] fn codemp(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(version, m)?)?; m.add_function(wrap_pyfunction!(init, m)?)?; - m.add_function(wrap_pyfunction!(get_default_config, m)?)?; m.add_function(wrap_pyfunction!(connect, m)?)?; m.add_function(wrap_pyfunction!(set_logger, m)?)?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; + m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/src/ffi/python/workspace.rs b/src/ffi/python/workspace.rs index 3ceeebc..8d3c525 100644 --- a/src/ffi/python/workspace.rs +++ b/src/ffi/python/workspace.rs @@ -1,4 +1,5 @@ use crate::api::controller::AsyncReceiver; +use crate::api::User; use crate::buffer::Controller as BufferController; use crate::cursor::Controller as CursorController; use crate::workspace::Workspace; @@ -11,21 +12,21 @@ use super::Promise; #[pymethods] impl Workspace { // join a workspace - #[pyo3(name = "create")] - fn pycreate(&self, py: Python, path: String) -> PyResult { + #[pyo3(name = "create_buffer")] + fn pycreate_buffer(&self, py: Python, path: String) -> PyResult { let this = self.clone(); - a_sync_allow_threads!(py, this.create(path.as_str()).await) + a_sync_allow_threads!(py, this.create_buffer(path.as_str()).await) } - #[pyo3(name = "attach")] - fn pyattach(&self, py: Python, path: String) -> PyResult { + #[pyo3(name = "attach_buffer")] + fn pyattach_buffer(&self, py: Python, path: String) -> PyResult { let this = self.clone(); - a_sync_allow_threads!(py, this.attach(path.as_str()).await) + a_sync_allow_threads!(py, this.attach_buffer(path.as_str()).await) } - #[pyo3(name = "detach")] - fn pydetach(&self, path: String) -> bool { - self.detach(path.as_str()) + #[pyo3(name = "detach_buffer")] + fn pydetach_buffer(&self, path: String) -> bool { + self.detach_buffer(path.as_str()) } #[pyo3(name = "fetch_buffers")] @@ -40,17 +41,17 @@ impl Workspace { a_sync_allow_threads!(py, this.fetch_users().await) } - #[pyo3(name = "list_buffer_users")] - fn pylist_buffer_users(&self, py: Python, path: String) -> PyResult { + #[pyo3(name = "fetch_buffer_users")] + fn pyfetch_buffer_users(&self, py: Python, path: String) -> PyResult { // crate::Result> let this = self.clone(); - a_sync_allow_threads!(py, this.list_buffer_users(path.as_str()).await) + a_sync_allow_threads!(py, this.fetch_buffer_users(path.as_str()).await) } - #[pyo3(name = "delete")] - fn pydelete(&self, py: Python, path: String) -> PyResult { + #[pyo3(name = "delete_buffer")] + fn pydelete_buffer(&self, py: Python, path: String) -> PyResult { let this = self.clone(); - a_sync_allow_threads!(py, this.delete(path.as_str()).await) + a_sync_allow_threads!(py, this.delete_buffer(path.as_str()).await) } #[pyo3(name = "id")] @@ -63,24 +64,24 @@ impl Workspace { self.cursor() } - #[pyo3(name = "buffer_by_name")] - fn pybuffer_by_name(&self, path: String) -> Option { - self.buffer_by_name(path.as_str()) + #[pyo3(name = "get_buffer")] + fn pyget_buffer(&self, path: String) -> Option { + self.get_buffer(path.as_str()) } - #[pyo3(name = "buffer_list")] - fn pybuffer_list(&self) -> Vec { - self.buffer_list() + #[pyo3(name = "active_buffers")] + fn pyactive_buffers(&self) -> Vec { + self.active_buffers() } - #[pyo3(name = "filetree")] - #[pyo3(signature = (filter=None, strict=false))] - fn pyfiletree(&self, filter: Option<&str>, strict: bool) -> Vec { - self.filetree(filter, strict) + #[pyo3(name = "search_buffers")] + #[pyo3(signature = (filter=None))] + fn pysearch_buffers(&self, filter: Option<&str>) -> Vec { + self.search_buffers(filter) } #[pyo3(name = "user_list")] - fn pyuser_list(&self) -> Vec { + fn pyuser_list(&self) -> Vec { self.user_list() } diff --git a/src/lib.rs b/src/lib.rs index 8ddf2c8..3179b7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,13 +32,13 @@ //! ``` //! //! A [`Client`] can acquire a [`Workspace`] handle by joining an existing one it can access with -//! [`Client::join_workspace`] or create a new one with [`Client::create_workspace`]. +//! [`Client::attach_workspace`] or create a new one with [`Client::create_workspace`]. //! //! ```no_run //! # async { //! # let client = codemp::Client::connect(codemp::api::Config::new("", "")).await.unwrap(); //! client.create_workspace("my-workspace").await.expect("failed to create workspace!"); -//! let workspace = client.join_workspace("my-workspace").await.expect("failed to attach!"); +//! let workspace = client.attach_workspace("my-workspace").await.expect("failed to attach!"); //! # }; //! ``` //! @@ -49,7 +49,7 @@ //! # async { //! # let client = codemp::Client::connect(codemp::api::Config::new("", "")).await.unwrap(); //! # client.create_workspace("").await.unwrap(); -//! # let workspace = client.join_workspace("").await.unwrap(); +//! # let workspace = client.attach_workspace("").await.unwrap(); //! use codemp::api::controller::{AsyncSender, AsyncReceiver}; // needed to access trait methods //! let cursor = workspace.cursor(); //! let event = cursor.recv().await.expect("disconnected while waiting for event!"); @@ -65,14 +65,14 @@ //! # async { //! # let client = codemp::Client::connect(codemp::api::Config::new("", "")).await.unwrap(); //! # client.create_workspace("").await.unwrap(); -//! # let workspace = client.join_workspace("").await.unwrap(); +//! # let workspace = client.attach_workspace("").await.unwrap(); //! # use codemp::api::controller::{AsyncSender, AsyncReceiver}; -//! let buffer = workspace.attach("/some/file.txt").await.expect("failed to attach"); +//! let buffer = workspace.attach_buffer("/some/file.txt").await.expect("failed to attach"); //! buffer.content(); // force-sync //! if let Some(mut update) = buffer.try_recv().await.unwrap() { //! println!( //! "content: {}, span: {}-{}", -//! update.change.content, update.change.start, update.change.end +//! update.change.content, update.change.start_idx, update.change.end_idx //! ); //! buffer.ack(update.version); //! } // if None, no changes are currently available diff --git a/src/prelude.rs b/src/prelude.rs index 71bb813..5f96584 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -2,7 +2,7 @@ //! All-in-one renamed imports with `use codemp::prelude::*`. pub use crate::api::{ - controller::AsyncReceiver as CodempAsyncReceiver, controller::AsyncSender as CodempAsyncSender, + AsyncReceiver as CodempAsyncReceiver, AsyncSender as CodempAsyncSender, BufferUpdate as CodempBufferUpdate, Config as CodempConfig, Controller as CodempController, Cursor as CodempCursor, Event as CodempEvent, Selection as CodempSelection, TextChange as CodempTextChange, User as CodempUser, diff --git a/src/workspace.rs b/src/workspace.rs index fe14388..f8054fc 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -26,7 +26,7 @@ use codemp_proto::{ }; use dashmap::{DashMap, DashSet}; -use std::{collections::BTreeSet, sync::Arc}; +use std::sync::Arc; use tokio::sync::{mpsc, mpsc::error::TryRecvError}; use tonic::Streaming; use uuid::Uuid; @@ -140,7 +140,7 @@ impl Workspace { } /// Create a new buffer in the current workspace. - pub async fn create(&self, path: &str) -> RemoteResult<()> { + pub async fn create_buffer(&self, path: &str) -> RemoteResult<()> { let mut workspace_client = self.0.services.ws(); workspace_client .create_buffer(tonic::Request::new(BufferNode { @@ -158,7 +158,7 @@ impl Workspace { } /// Attach to a buffer and return a handle to it. - pub async fn attach(&self, path: &str) -> ConnectionResult { + pub async fn attach_buffer(&self, path: &str) -> ConnectionResult { let mut worskspace_client = self.0.services.ws(); let request = tonic::Request::new(BufferNode { path: path.to_string(), @@ -190,7 +190,7 @@ impl Workspace { /// If this method returns `false` you have a dangling ref, maybe just waiting for garbage /// collection or maybe preventing the controller from being dropped completely #[allow(clippy::redundant_pattern_matching)] // all cases are clearer this way - pub fn detach(&self, path: &str) -> bool { + pub fn detach_buffer(&self, path: &str) -> bool { match self.0.buffers.remove(path) { None => true, // noop: we werent attached in the first place Some((_name, controller)) => match Arc::into_inner(controller.0) { @@ -201,45 +201,48 @@ impl Workspace { } /// Re-fetch the list of available buffers in the workspace. - pub async fn fetch_buffers(&self) -> RemoteResult<()> { + pub async fn fetch_buffers(&self) -> RemoteResult> { let mut workspace_client = self.0.services.ws(); - let buffers = workspace_client + let resp = workspace_client .list_buffers(tonic::Request::new(Empty {})) .await? - .into_inner() - .buffers; + .into_inner(); + + let mut out = Vec::new(); self.0.filetree.clear(); - for b in buffers { - self.0.filetree.insert(b.path); + for b in resp.buffers { + self.0.filetree.insert(b.path.clone()); + out.push(b.path); } - Ok(()) + Ok(out) } /// Re-fetch the list of all users in the workspace. - pub async fn fetch_users(&self) -> RemoteResult<()> { + pub async fn fetch_users(&self) -> RemoteResult> { let mut workspace_client = self.0.services.ws(); - let users = BTreeSet::from_iter( - workspace_client - .list_users(tonic::Request::new(Empty {})) - .await? - .into_inner() - .users - .into_iter() - .map(User::from), - ); + let users = workspace_client + .list_users(tonic::Request::new(Empty {})) + .await? + .into_inner() + .users + .into_iter() + .map(User::from); + + let mut result = Vec::new(); self.0.users.clear(); for u in users { - self.0.users.insert(u.id, u); + self.0.users.insert(u.id, u.clone()); + result.push(u); } - Ok(()) + Ok(result) } - /// Get a list of the [User]s attached to a specific buffer. - pub async fn list_buffer_users(&self, path: &str) -> RemoteResult> { + /// Fetch a list of the [User]s attached to a specific buffer. + pub async fn fetch_buffer_users(&self, path: &str) -> RemoteResult> { let mut workspace_client = self.0.services.ws(); let buffer_users = workspace_client .list_buffer_users(tonic::Request::new(BufferNode { @@ -256,8 +259,8 @@ impl Workspace { } /// Delete a buffer. - pub async fn delete(&self, path: &str) -> RemoteResult<()> { - self.detach(path); // just in case + pub async fn delete_buffer(&self, path: &str) -> RemoteResult<()> { + self.detach_buffer(path); // just in case let mut workspace_client = self.0.services.ws(); workspace_client @@ -285,13 +288,13 @@ impl Workspace { /// Return a handle to the [buffer::Controller] with the given path, if present. // #[cfg_attr(feature = "js", napi)] // https://github.com/napi-rs/napi-rs/issues/1120 - pub fn buffer_by_name(&self, path: &str) -> Option { + pub fn get_buffer(&self, path: &str) -> Option { self.0.buffers.get(path).map(|x| x.clone()) } /// Get a list of all the currently attached buffers. // #[cfg_attr(feature = "js", napi)] // https://github.com/napi-rs/napi-rs/issues/1120 - pub fn buffer_list(&self) -> Vec { + pub fn active_buffers(&self) -> Vec { self.0 .buffers .iter() @@ -300,31 +303,23 @@ impl Workspace { } /// Get all names of users currently in this workspace - pub fn user_list(&self) -> Vec { + pub fn user_list(&self) -> Vec { self.0 .users .iter() - .map(|elem| elem.value().name.clone()) + .map(|elem| elem.value().clone()) .collect() } /// Get the filetree as it is currently cached. /// A filter may be applied, and it may be strict (equality check) or not (starts_with check). // #[cfg_attr(feature = "js", napi)] // https://github.com/napi-rs/napi-rs/issues/1120 - pub fn filetree(&self, filter: Option<&str>, strict: bool) -> Vec { + pub fn search_buffers(&self, filter: Option<&str>) -> Vec { let mut tree = self .0 .filetree .iter() - .filter(|f| { - filter.map_or(true, |flt| { - if strict { - f.as_str() == flt - } else { - f.starts_with(flt) - } - }) - }) + .filter(|f| filter.map_or(true, |flt| f.starts_with(flt))) .map(|f| f.clone()) .collect::>(); tree.sort();