diff --git a/.gitignore b/.gitignore
index 44bb574..a036acb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
*~
.DS_Store
*.swp
-/target
+**/target
+**/Cargo.lock
+woweb/assets/dist_text*
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..4312957
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/poc.iml b/.idea/poc.iml
new file mode 100644
index 0000000..53e954b
--- /dev/null
+++ b/.idea/poc.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.woodpecker.yml b/.woodpecker.yml
index 014ae3a..8fec64f 100644
--- a/.woodpecker.yml
+++ b/.woodpecker.yml
@@ -4,7 +4,7 @@ pipeline:
when:
event: tag
settings:
- repo: docker.io/thallian/woweb-poc
+ repo: docker.io/thallian/woweb
tags: latest,${CI_COMMIT_SHA:0:8},${CI_COMMIT_TAG=pre}
dockerfile: Dockerfile
username:
diff --git a/Cargo.lock b/Cargo.lock
index e379c25..93086bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,10 +3,19 @@
version = 3
[[package]]
-name = "async-trait"
-version = "0.1.64"
+name = "aho-corasick"
+version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
dependencies = [
"proc-macro2",
"quote",
@@ -21,9 +30,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
-version = "0.6.7"
+version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591"
+checksum = "8582122b8edba2af43eaf6b80dbfd33f421b5a0eb3a3113d21bc096ac5b44faf"
dependencies = [
"async-trait",
"axum-core",
@@ -51,16 +60,16 @@ dependencies = [
"tokio",
"tokio-tungstenite",
"tower",
- "tower-http",
+ "tower-http 0.4.0",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34"
+checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e"
dependencies = [
"async-trait",
"bytes",
@@ -87,7 +96,7 @@ dependencies = [
"pin-project-lite",
"tokio",
"tower",
- "tower-http",
+ "tower-http 0.3.5",
"tower-layer",
"tower-service",
]
@@ -119,6 +128,12 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
[[package]]
name = "byteorder"
version = "1.4.3"
@@ -146,6 +161,24 @@ dependencies = [
"libc",
]
+[[package]]
+name = "crdts"
+version = "7.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e30e1170960eddf5392c448ff5b190a49cfcb28d6c79f789f2b4e30b571f8f8"
+dependencies = [
+ "num",
+ "quickcheck",
+ "serde",
+ "tiny-keccak",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -166,6 +199,33 @@ dependencies = [
"crypto-common",
]
+[[package]]
+name = "dist_text"
+version = "0.1.0"
+dependencies = [
+ "crdts",
+]
+
+[[package]]
+name = "dist_text_js"
+version = "0.1.0"
+dependencies = [
+ "dist_text",
+ "serde",
+ "serde-wasm-bindgen",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+dependencies = [
+ "log",
+ "regex",
+]
+
[[package]]
name = "fnv"
version = "1.0.7"
@@ -280,6 +340,17 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
[[package]]
name = "getrandom"
version = "0.2.8"
@@ -288,7 +359,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
@@ -404,6 +475,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -480,8 +560,8 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [
"libc",
"log",
- "wasi",
- "windows-sys 0.45.0",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys",
]
[[package]]
@@ -494,6 +574,85 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "num"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+ "serde",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
+dependencies = [
+ "num-traits",
+ "serde",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+ "serde",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "num_cpus"
version = "1.15.0"
@@ -536,7 +695,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
- "windows-sys 0.45.0",
+ "windows-sys",
]
[[package]]
@@ -592,6 +751,18 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "quickcheck"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f"
+dependencies = [
+ "env_logger",
+ "log",
+ "rand 0.7.3",
+ "rand_core 0.5.1",
+]
+
[[package]]
name = "quote"
version = "1.0.23"
@@ -601,6 +772,19 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+]
+
[[package]]
name = "rand"
version = "0.8.5"
@@ -608,8 +792,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
]
[[package]]
@@ -619,7 +813,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
]
[[package]]
@@ -628,7 +831,16 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom",
+ "getrandom 0.2.8",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
]
[[package]]
@@ -646,6 +858,8 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
+ "aho-corasick",
+ "memchr",
"regex-syntax",
]
@@ -666,15 +880,15 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "rustversion"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
+checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
[[package]]
name = "ryu"
-version = "1.0.12"
+version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "scopeguard"
@@ -684,18 +898,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
-version = "1.0.152"
+version = "1.0.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+checksum = "3a382c72b4ba118526e187430bb4963cd6d55051ebf13d9b25574d379cc98d20"
dependencies = [
"serde_derive",
]
[[package]]
-name = "serde_derive"
-version = "1.0.152"
+name = "serde-wasm-bindgen"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ef476a5790f0f6decbc66726b6e5d63680ed518283e64c7df415989d880954f"
dependencies = [
"proc-macro2",
"quote",
@@ -704,9 +929,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.93"
+version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
+checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
dependencies = [
"itoa",
"ryu",
@@ -715,9 +940,9 @@ dependencies = [
[[package]]
name = "serde_path_to_error"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341"
+checksum = "db0969fff533976baadd92e08b1d102c5a3d8a8049eadfd69d4d1e3c5b2ed189"
dependencies = [
"serde",
]
@@ -780,9 +1005,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "socket2"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
@@ -790,9 +1015,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.107"
+version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
@@ -807,18 +1032,18 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "thiserror"
-version = "1.0.38"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
+checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.38"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
+checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
"proc-macro2",
"quote",
@@ -835,6 +1060,15 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
[[package]]
name = "tinyvec"
version = "1.6.0"
@@ -852,9 +1086,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.25.0"
+version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
+checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
dependencies = [
"autocfg",
"bytes",
@@ -867,7 +1101,7 @@ dependencies = [
"signal-hook-registry",
"socket2",
"tokio-macros",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -942,12 +1176,30 @@ dependencies = [
"pin-project-lite",
"tokio",
"tokio-util",
- "tower",
"tower-layer",
"tower-service",
"tracing",
]
+[[package]]
+name = "tower-http"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-range-header",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
[[package]]
name = "tower-layer"
version = "0.3.2"
@@ -1041,7 +1293,7 @@ dependencies = [
"http",
"httparse",
"log",
- "rand",
+ "rand 0.8.5",
"sha1",
"thiserror",
"url",
@@ -1071,9 +1323,9 @@ checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-ident"
-version = "1.0.6"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-normalization"
@@ -1129,12 +1381,72 @@ dependencies = [
"try-lock",
]
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -1157,21 +1469,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-[[package]]
-name = "windows-sys"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
[[package]]
name = "windows-sys"
version = "0.45.0"
@@ -1239,17 +1536,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
-name = "woweb-poc"
+name = "woweb"
version = "0.1.0"
dependencies = [
"axum",
"axum-extra",
+ "dist_text",
"futures",
"headers",
"serde",
"serde_json",
"tokio",
- "tower-http",
+ "tower-http 0.3.5",
"tracing",
"tracing-subscriber",
"unicode-segmentation",
diff --git a/Cargo.toml b/Cargo.toml
index 752e976..be4055b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,17 +1,7 @@
-[package]
-name = "woweb-poc"
-version = "0.1.0"
-edition = "2021"
+[workspace]
-[dependencies]
-axum = { version = "0.6.7", features = ["ws", "headers"] }
-axum-extra = { version = "0.5.0", features = ["spa"] }
-futures = "0.3.26"
-headers = "0.3.8"
-serde = { version = "1.0.152", features = ["derive"] }
-serde_json = "1.0.93"
-tokio = { version = "1.25.0", features = ["full"] }
-tower-http = { version = "0.3.5", features = ["trace"] }
-tracing = "0.1.37"
-tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
-unicode-segmentation = "1.10.1"
+members = [
+ "dist_text",
+ "dist_text_js",
+ "woweb"
+]
diff --git a/Dockerfile b/Dockerfile
index c877132..af8536c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,20 +7,20 @@ ADD . /src
WORKDIR /src
RUN cargo build --release
-RUN strip target/release/woweb-poc
+RUN strip target/release/woweb
RUN adduser -D woweb
-RUN sed -i 's|ws://localhost:3000/ws|wss://woweb.vanwa.ch/ws|' assets/index.js
+RUN sed -i 's|ws://localhost:3000/ws|wss://woweb.vanwa.ch/ws|' woweb/assets/index.js
FROM scratch
COPY --from=builder /etc/passwd /etc/passwd
-COPY --from=builder /src/target/release/woweb-poc "/opt/woweb-poc"
-COPY --from=builder /src/assets "/opt/assets"
+COPY --from=builder /src/target/release/woweb "/opt/woweb"
+COPY --from=builder /src/woweb/assets "/opt/assets"
EXPOSE 3000
WORKDIR /opt
USER woweb
-CMD ["/opt/woweb-poc", "0.0.0.0:3000"]
+CMD ["/opt/woweb", "0.0.0.0:3000"]
diff --git a/assets/index.js b/assets/index.js
deleted file mode 100644
index ce08f4d..0000000
--- a/assets/index.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const areaId = "doctext";
-const validEvents = [
- "insertText",
- "insertFromPaste",
- "insertLineBreak",
- "insertCompositionText",
- "deleteContentBackward",
- "deleteContentForward",
-];
-
-let selectionStart = 0;
-let selectionEnd = 0;
-
-let area;
-let ws;
-
-const uuid = self.crypto.randomUUID();
-const wsUrl = "ws://localhost:3000/ws";
-
-const textEncoder = new TextEncoder();
-const textDecoder = new TextDecoder();
-
-function setup() {
- area = document.querySelector(`#${areaId}`);
- ws = new WebSocket(wsUrl);
-
- setupUi();
- setupWs();
-}
-
-function setupUi() {
- document.addEventListener("selectionchange", onSelectionChange, false);
- area.addEventListener("beforeinput", onInput, false);
-}
-
-function setupWs() {
- ws.onclose = function (e) {
- console.log(e);
- setTimeout(() => {
- ws = new WebSocket(wsUrl);
- }, 2000);
- };
-
- ws.onmessage = function (e) {
- let payload = JSON.parse(e.data);
- if (payload.client !== uuid) {
- console.log(payload.doc);
- area.value = payload.doc;
- }
- };
-}
-
-function onSelectionChange() {
- const activeElement = document.activeElement;
-
- if (activeElement && activeElement.id === areaId) {
- console.debug("onSelectionChange", area);
- selectionStart = area.selectionStart;
- selectionEnd = area.selectionEnd;
- }
-}
-
-function onInput(event) {
- if (!validEvents.includes(event.inputType)) return;
-
- const payload = {
- client: uuid,
- action: event.inputType,
- data: event.data,
- start: selectionStart,
- end: selectionEnd,
- };
-
- ws.send(JSON.stringify(payload));
-
- console.log(selectionStart, selectionEnd);
-
- // workaround for differences between firefox and chrome
- // chrome does not fire selectionchange events on backspace/delete events,
- // while firefox does
- if (event.inputType === "deleteContentBackward") {
- selectionStart = area.selectionStart - 1;
- selectionEnd = area.selectionEnd - 1;
- }
-}
-
-setup();
diff --git a/dist_text/Cargo.toml b/dist_text/Cargo.toml
new file mode 100644
index 0000000..640b012
--- /dev/null
+++ b/dist_text/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "dist_text"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+crdts = "7.3.0"
diff --git a/dist_text/src/lib.rs b/dist_text/src/lib.rs
new file mode 100644
index 0000000..99540ba
--- /dev/null
+++ b/dist_text/src/lib.rs
@@ -0,0 +1,3 @@
+pub mod text;
+
+pub use crdts;
\ No newline at end of file
diff --git a/dist_text/src/text.rs b/dist_text/src/text.rs
new file mode 100644
index 0000000..e850594
--- /dev/null
+++ b/dist_text/src/text.rs
@@ -0,0 +1,180 @@
+use std::fmt::{Display, Formatter};
+use crdts::{CmRDT, List};
+use crdts::list::Op;
+
+#[derive(Clone)]
+pub struct Text {
+ inner: List,
+}
+
+impl Default for Text {
+ fn default() -> Self {
+ Self {
+ inner: List::new()
+ }
+ }
+}
+
+impl Display for Text {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let str_bytes: Vec = self.inner.to_owned().read_into();
+ let str_repr = String::from_utf16_lossy(&str_bytes);
+ write!(f, "{}", str_repr)
+ }
+}
+
+impl Text {
+ pub fn apply_ops(&mut self, ops: Vec>) {
+ ops.iter().for_each(move |op| self.inner.apply(op.to_owned()));
+ }
+
+ pub fn insert_linebreak(&mut self, start: usize, end: usize, src: &str) -> Vec> {
+ self.insert(start, end, "\n", src)
+ }
+
+ pub fn insert(&mut self, start: usize, end: usize, data: &str, src: &str) -> Vec> {
+ let mut ops: Vec> = Vec::new();
+
+ if start < end {
+ let mut delete_ops = self.delete(start, end, src);
+ ops.append(&mut delete_ops);
+ }
+
+ let data: Vec = data.encode_utf16().collect();
+
+ let mut insert_idx = start;
+ for byte in data.iter() {
+ let op = self.inner.insert_index(insert_idx, *byte, src.to_owned());
+ self.inner.apply(op.to_owned());
+ insert_idx += 1;
+
+ ops.push(op);
+ }
+
+ ops
+ }
+
+ pub fn delete_backward(&mut self, start: usize, end: usize, src: &str) -> Vec> {
+ if (start == 0 && start == end) || end > self.inner.len() { return Vec::new(); }
+
+ let (end, start) = if start == end { (start, start - 1) } else { (end, start) };
+ self.delete(start, end, src)
+ }
+
+ pub fn delete_forward(&mut self, start: usize, end: usize, src: &str) -> Vec> {
+ if start >= self.inner.len() { return Vec::new(); }
+
+ let end = if start == end { start + 1 } else { end };
+ self.delete(start, end, src)
+ }
+
+ pub fn delete(&mut self, start: usize, end: usize, src: &str) -> Vec> {
+ let mut start_idx = start;
+ let mut end_idx = end;
+
+ while let Some(byte) = self.inner.position(start_idx) {
+ if !matches!(byte, 0xDC00..=0xDFFF) {
+ break;
+ }
+ start_idx -= 1;
+ }
+
+ if let Some(end_byte) = self.inner.position(end_idx - 1) {
+ if end_idx < self.inner.len() && end_byte > &0xD800 && end_byte < &0xDC00 {
+ end_idx += 1;
+ }
+ }
+
+ let mut ops: Vec> = Vec::new();
+
+ for _ in start_idx..end_idx {
+ if let Some(op) = self.inner.delete_index(start_idx, src.to_owned()) {
+ self.inner.apply(op.to_owned());
+ ops.push(op);
+ }
+ }
+
+ ops
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::text::Text;
+
+ #[test]
+ fn insert_delete() {
+ let mut t = Text::new();
+ t.insert(0, 0, "Hello", "A");
+ t.insert(5, 5, "world!", "A");
+ t.insert(5, 5, ", ", "B");
+
+ assert_eq!(t.to_string(), "Hello, world!");
+
+ t.insert(7, 12, "🌍", "A");
+ assert_eq!(t.to_string(), "Hello, 🌍!");
+
+ t.delete(8, 9, "B");
+ t.insert(7, 7, "🚀", "C");
+ assert_eq!(t.to_string(), "Hello, 🚀!");
+ }
+
+ #[test]
+ fn backspace() {
+ let mut t = Text::new();
+ t.insert(0, 0, "Hello", "A");
+
+ t.delete_backward(5, 5, "A");
+ assert_eq!(t.to_string(), "Hell");
+
+ t.delete_backward(0, 4, "A");
+ assert_eq!(t.to_string(), "");
+ }
+
+ #[test]
+ fn delete() {
+ let mut t = Text::new();
+ t.insert(0, 0, "Hello", "A");
+
+ t.delete_forward(4, 4, "A");
+ assert_eq!(t.to_string(), "Hell");
+
+ t.delete_forward(0, 4, "A");
+ assert_eq!(t.to_string(), "");
+ }
+
+ #[test]
+ fn linebreak() {
+ let mut t = Text::new();
+ t.insert(0, 0, "Hello, world!", "A");
+ t.insert_linebreak(6, 6, "A");
+
+ assert_eq!(t.to_string(), "Hello,\n world!");
+
+ t.delete_backward(7, 7, "B");
+ t.insert_linebreak(6, 7, "A");
+
+ assert_eq!(t.to_string(), "Hello,\nworld!");
+ }
+
+ #[test]
+ fn apply_ops() {
+ let mut t_a = Text::new();
+ let ops_a = t_a.insert(0, 0, "Hello, world!", "A");
+
+ let mut t_b = Text::new();
+ t_b.apply_ops(ops_a);
+
+ assert_eq!(t_b.to_string(), "Hello, world!");
+
+ let ops_a = t_a.insert(7,7, "distributed ", "A");
+ t_b.apply_ops(ops_a);
+
+ let ops_b = t_b.insert(7,7, "cruel ", "B");
+ t_a.apply_ops(ops_b);
+
+
+ assert_eq!(t_a.to_string(), "Hello, cruel distributed world!");
+ assert_eq!(t_b.to_string(), "Hello, cruel distributed world!");
+ }
+}
diff --git a/dist_text_js/Cargo.toml b/dist_text_js/Cargo.toml
new file mode 100644
index 0000000..cecd323
--- /dev/null
+++ b/dist_text_js/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "dist_text_js"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+dist_text = { path = "../dist_text" }
+serde = { version = "1.0.153", features = ["derive"] }
+serde-wasm-bindgen = "0.5.0"
+wasm-bindgen = "0.2.84"
\ No newline at end of file
diff --git a/dist_text_js/src/lib.rs b/dist_text_js/src/lib.rs
new file mode 100644
index 0000000..a129264
--- /dev/null
+++ b/dist_text_js/src/lib.rs
@@ -0,0 +1,38 @@
+use wasm_bindgen::prelude::*;
+use dist_text::crdts::list::Op;
+use dist_text::text::Text;
+
+#[wasm_bindgen(getter_with_clone)]
+pub struct State {
+ pub text: String,
+
+ #[wasm_bindgen(skip)]
+ pub inner: Text,
+}
+
+#[wasm_bindgen]
+impl State {
+ // wasm does not support trait impls yet, hence the default implementation here
+ pub fn default() -> Self {
+ Self { text: String::default(), inner: Text::default() }
+ }
+
+ pub fn apply(&mut self, ops: JsValue) -> String {
+ let ops: Vec> = serde_wasm_bindgen::from_value(ops).unwrap_or(Vec::new());
+ self.inner.apply_ops(ops);
+
+ self.inner.to_string()
+ }
+
+ pub fn execute(&mut self, action: &str, start: usize, end: usize, data: &str, src: &str) -> JsValue {
+ let ops = match action {
+ "insertText" | "insertFromPaste" => self.inner.insert(start, end, data, src),
+ "insertLineBreak" => self.inner.insert_linebreak(start, end, src),
+ "deleteContentBackward" => self.inner.delete_backward(start, end, src),
+ "deleteContentForward" => self.inner.delete_forward(start, end, src),
+ &_ => Vec::default()
+ };
+
+ serde_wasm_bindgen::to_value(&ops).unwrap_or_default()
+ }
+}
diff --git a/src/action.rs b/src/action.rs
deleted file mode 100644
index ee5f6c2..0000000
--- a/src/action.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use std::sync::Arc;
-
-use serde::{Deserialize, Serialize};
-
-use crate::actions::delete::{DeleteContentBackward, DeleteContentForward};
-use crate::actions::insert::{InsertText, InsertFromPaste, InsertLineBreak};
-use crate::actions::ActionRunner;
-use crate::AppState;
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-enum ActionType {
- InsertText,
- InsertFromPaste,
- InsertLineBreak,
- DeleteContentBackward,
- DeleteContentForward,
-}
-
-#[derive(Serialize, Deserialize)]
-pub(crate) struct Action {
- pub(crate) client: String,
- data: Option,
- action: ActionType,
- start: usize,
- end: usize,
-}
-
-impl Action {
- pub(crate) async fn execute(&self, state: Arc) {
- {
- let mut doc = state.doc.write().await;
- let action: Box = match self.action {
- ActionType::InsertText => Box::new(InsertText),
- ActionType::InsertFromPaste => Box::new(InsertFromPaste),
- ActionType::InsertLineBreak => Box::new(InsertLineBreak),
- ActionType::DeleteContentBackward => Box::new(DeleteContentBackward),
- ActionType::DeleteContentForward => Box::new(DeleteContentForward),
- };
-
- let data = match self.data.to_owned() {
- None => Vec::new(),
- Some(data) => data.encode_utf16().collect()
- };
- *doc = action.run(self.start, self.end, data, doc.to_owned());
- }
- }
-}
diff --git a/src/actions/delete.rs b/src/actions/delete.rs
deleted file mode 100644
index 5880178..0000000
--- a/src/actions/delete.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use super::ActionRunner;
-
-pub(crate) struct DeleteContentBackward;
-
-pub(crate) struct DeleteContentForward;
-
-fn delete(start: usize, end: usize, mut doc: Vec) -> Vec {
- if start > doc.len() + 1 {
- return doc;
- }
-
- let mut start_idx = start;
- let mut end_idx = end;
- unsafe {
- while start_idx > 0 && matches!(doc.get_unchecked(start_idx), 0xDC00..=0xDFFF) {
- start_idx -= 1;
- }
-
- let end_byte = doc.get_unchecked(end_idx - 1);
- if end_idx < doc.len() && end_byte > &0xD800 && end_byte < &0xDC00 {
- end_idx += 1;
- }
- }
-
- doc.drain(start_idx..end_idx);
- doc
-}
-
-impl ActionRunner for DeleteContentBackward {
- fn run(&self, start: usize, end: usize, _data: Vec, doc: Vec) -> Vec {
- if (start == 0 && start == end ) || end > doc.len() { return doc; }
- let (end, start) = if start == end { (start, start - 1) } else { (end, start) };
-
- delete(start, end, doc)
- }
-}
-
-impl ActionRunner for DeleteContentForward {
- fn run(&self, start: usize, end: usize, _data: Vec, doc: Vec) -> Vec {
- if start >= doc.len() { return doc; }
-
- let end = if start == end { start + 1 } else { end };
- delete(start, end, doc)
- }
-}
diff --git a/src/actions/insert.rs b/src/actions/insert.rs
deleted file mode 100644
index a99ad61..0000000
--- a/src/actions/insert.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use super::ActionRunner;
-
-pub(crate) struct InsertText;
-pub(crate) struct InsertFromPaste;
-pub(crate) struct InsertLineBreak;
-
-const NL_BYTES: [u16; 1] = [10];
-
-fn insert(start: usize, end: usize, data: Vec, mut doc: Vec) -> Vec {
- if doc.len() < start || data.is_empty() {
- return doc;
- }
-
- if start < end {
- doc.drain(start..end);
- }
-
- doc.splice(start..start, data);
- doc
-}
-
-impl ActionRunner for InsertText {
- fn run(&self, start: usize, end: usize, data: Vec, doc: Vec) -> Vec {
- insert(start, end, data, doc)
- }
-}
-
-impl ActionRunner for InsertFromPaste {
- fn run(&self, start: usize, end: usize, data: Vec, doc: Vec) -> Vec {
- insert(start, end, data, doc)
- }
-}
-
-impl ActionRunner for InsertLineBreak {
- fn run(&self, start: usize, _end: usize, _data: Vec, doc: Vec) -> Vec {
- insert(start, start, Vec::from(NL_BYTES), doc)
- }
-}
diff --git a/src/actions/mod.rs b/src/actions/mod.rs
deleted file mode 100644
index d33bf93..0000000
--- a/src/actions/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub(crate) mod delete;
-pub(crate) mod insert;
-
-pub(crate) trait ActionRunner {
- fn run(&self, start: usize, end: usize, data: Vec, doc: Vec) -> Vec;
-}
diff --git a/woweb/.idea/.gitignore b/woweb/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/woweb/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/woweb/.idea/modules.xml b/woweb/.idea/modules.xml
new file mode 100644
index 0000000..ea8c3ec
--- /dev/null
+++ b/woweb/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/woweb/.idea/vcs.xml b/woweb/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/woweb/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/woweb/.idea/woweb-poc.iml b/woweb/.idea/woweb-poc.iml
new file mode 100644
index 0000000..c254557
--- /dev/null
+++ b/woweb/.idea/woweb-poc.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/woweb/Cargo.toml b/woweb/Cargo.toml
new file mode 100644
index 0000000..8c001b1
--- /dev/null
+++ b/woweb/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "woweb"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+
+[dependencies]
+dist_text = { path = "../dist_text" }
+axum = { version = "0.6.7", features = ["ws", "headers"] }
+axum-extra = { version = "0.5.0", features = ["spa"] }
+futures = "0.3.26"
+headers = "0.3.8"
+serde = { version = "1.0.152", features = ["derive"] }
+serde_json = "1.0.93"
+tokio = { version = "1.25.0", features = ["full"] }
+tower-http = { version = "0.3.5", features = ["trace"] }
+tracing = "0.1.37"
+tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
+unicode-segmentation = "1.10.1"
diff --git a/assets/index.html b/woweb/assets/index.html
similarity index 82%
rename from assets/index.html
rename to woweb/assets/index.html
index b2d722f..ce0b292 100644
--- a/assets/index.html
+++ b/woweb/assets/index.html
@@ -6,6 +6,6 @@
-
+