diff options
| author | Xe <me@christine.website> | 2022-11-22 14:55:31 -0500 |
|---|---|---|
| committer | Xe <me@christine.website> | 2022-11-22 14:55:31 -0500 |
| commit | 4cb18058ba49416e8413f65e966dff002fcea4f7 (patch) | |
| tree | 0fc801caeaee09598bbca1529831fb8411c1a9c9 | |
| parent | c1fd35aa707fe8dfbd6ab1351e5e3f0564ad0b11 (diff) | |
| download | x-4cb18058ba49416e8413f65e966dff002fcea4f7.tar.xz x-4cb18058ba49416e8413f65e966dff002fcea4f7.zip | |
add mastosan package to turn Mastodon HTML to Slackdown
Signed-off-by: Xe <me@christine.website>
| -rw-r--r-- | flake.lock | 24 | ||||
| -rw-r--r-- | flake.nix | 22 | ||||
| -rw-r--r-- | go.mod | 1 | ||||
| -rw-r--r-- | go.sum | 2 | ||||
| -rw-r--r-- | web/mastosan/.cargo/config | 2 | ||||
| -rw-r--r-- | web/mastosan/.gitignore | 1 | ||||
| -rw-r--r-- | web/mastosan/Cargo.lock | 496 | ||||
| -rw-r--r-- | web/mastosan/Cargo.toml | 15 | ||||
| -rw-r--r-- | web/mastosan/README.md | 24 | ||||
| -rw-r--r-- | web/mastosan/mastosan.go | 64 | ||||
| -rw-r--r-- | web/mastosan/mastosan_test.go | 32 | ||||
| -rw-r--r-- | web/mastosan/src/main.rs | 48 | ||||
| -rw-r--r-- | web/mastosan/testdata/mastosan.wasm | bin | 0 -> 460001 bytes |
13 files changed, 729 insertions, 2 deletions
@@ -94,9 +94,33 @@ "gomod2nix": "gomod2nix", "nixpkgs": "nixpkgs", "portable-svc": "portable-svc", + "rust-overlay": "rust-overlay", "utils": "utils" } }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1669084742, + "narHash": "sha256-aLYwYVnrmEE1LVqd17v99CuqVmAZQrlgi2DVTAs4wFg=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "9652ef34c7439eca9f86cee11e94dbef5c9adb09", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, "utils": { "locked": { "lastModified": 1659877975, @@ -7,6 +7,12 @@ portable-svc.url = "git+https://tulpa.dev/cadey/portable-svc.git?ref=main"; ckiee.url = "github:ckiee/nixpkgs?ref=gpt2simple-py-init"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "utils"; + }; + gomod2nix = { url = "github:tweag/gomod2nix"; inputs.nixpkgs.follows = "nixpkgs"; @@ -14,7 +20,7 @@ }; }; - outputs = { self, nixpkgs, utils, gomod2nix, portable-svc, ckiee }@attrs: + outputs = { self, nixpkgs, utils, gomod2nix, portable-svc, ckiee, rust-overlay }@attrs: utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" @@ -31,7 +37,8 @@ }) gomod2nix.overlays.default portable-svc.overlay - (final: prev: self.packages.${system}) + rust-overlay.overlays.default + #(final: prev: self.packages.${system}) ]; }; ckieepkgs = import ckiee { inherit system; }; @@ -182,6 +189,17 @@ pkg-config libaom libavif + + cargo + cargo-watch + rustfmt + rust-analyzer + wasmtime + binaryen + (rust-bin.stable.latest.default.override { + extensions = [ "rust-src" ]; + targets = [ "wasm32-wasi" ]; + }) ]; }; }); @@ -122,6 +122,7 @@ require ( github.com/tcnksm/go-httpstat v0.2.0 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect + github.com/tetratelabs/wazero v1.0.0-pre.3 // indirect github.com/tjfoc/gmsm v1.0.1 // indirect github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect @@ -395,6 +395,8 @@ github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7S github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tetratelabs/wazero v1.0.0-pre.3 h1:Z5fbogMUGcERzaQb9mQU8+yJSy0bVvv2ce3dfR4wcZg= +github.com/tetratelabs/wazero v1.0.0-pre.3/go.mod h1:M8UDNECGm/HVjOfq0EOe4QfCY9Les1eq54IChMLETbc= github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef h1:7D6Nm4D6f0ci9yttWaKjM1TMAXrH5Su72dojqYGntFY= diff --git a/web/mastosan/.cargo/config b/web/mastosan/.cargo/config new file mode 100644 index 0000000..6b77899 --- /dev/null +++ b/web/mastosan/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasi" diff --git a/web/mastosan/.gitignore b/web/mastosan/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/web/mastosan/.gitignore @@ -0,0 +1 @@ +target diff --git a/web/mastosan/Cargo.lock b/web/mastosan/Cargo.lock new file mode 100644 index 0000000..a3449fc --- /dev/null +++ b/web/mastosan/Cargo.lock @@ -0,0 +1,496 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf", + "proc-macro2", + "quote", + "smallvec", + "syn", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "dtoa-short" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" +dependencies = [ + "dtoa", +] + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lol_html" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ff2adf9c54f4de7d66a9177ea7d27d5b8108503bb03d5b719869b8f4bc2ab2" +dependencies = [ + "bitflags", + "cfg-if", + "cssparser", + "encoding_rs", + "hashbrown", + "lazy_static", + "lazycell", + "memchr", + "safemem", + "selectors", + "thiserror", +] + +[[package]] +name = "mastosan" +version = "0.1.0" +dependencies = [ + "lol_html", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +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", + "rand_core", + "rand_hc", + "rand_pcg", +] + +[[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", +] + +[[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]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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" diff --git a/web/mastosan/Cargo.toml b/web/mastosan/Cargo.toml new file mode 100644 index 0000000..3aba4e8 --- /dev/null +++ b/web/mastosan/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mastosan" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lol_html = "0.3" + +[profile.release] +lto = true +opt-level = "z" +panic = "abort" + diff --git a/web/mastosan/README.md b/web/mastosan/README.md new file mode 100644 index 0000000..f3c5a00 --- /dev/null +++ b/web/mastosan/README.md @@ -0,0 +1,24 @@ +# mastosan + +Package mastosan takes Mastodon flavored HTML and emits Slack-flavored +markdown. + +It works by using an embedded Rust program compiled to WebAssembly. At +the time of writing this adds an extra 0.2 seconds to compile and run the +WebAssembly module, but this can probably be fixed with aggressive sync.Once +caching should this prove a problem in the real world. + +Building this Rust program outside of Nix flakes is UNSUPPORTED. + +To build the Rust module from inside the Nix flake: + + cargo install wasm-snip + cargo build --release + wasm-opt -Oz -o ./testdata/mastosan-pre-snip.wasm + wasm-snip --skip-producers-section --snip-rust-panicking-code -i ./testdata/mastosan-pre-snip.wasm ./testdata/mastosan.wasm + rm ./testdata/mastosan-pre-snip.wasm + +This adds about two megabytes to the resulting binary, including the AOT +WebAssembly runtime wazero: https://wazero.io/ + + diff --git a/web/mastosan/mastosan.go b/web/mastosan/mastosan.go new file mode 100644 index 0000000..87bee1d --- /dev/null +++ b/web/mastosan/mastosan.go @@ -0,0 +1,64 @@ +// Package mastosan takes Mastodon flavored HTML and emits Slack-flavored +// markdown. +// +// It works by using an embedded Rust program compiled to WebAssembly. At +// the time of writing this adds an extra 0.2 seconds to compile and run the +// WebAssembly module, but this can probably be fixed with aggressive sync.Once +// caching should this prove a problem in the real world. +// +// Building this Rust program outside of Nix flakes is UNSUPPORTED. +// +// To build the Rust module from inside the Nix flake: +// +// cargo install wasm-snip +// cargo build --release +// wasm-opt -Oz -o ./testdata/mastosan-pre-snip.wasm +// wasm-snip --skip-producers-section --snip-rust-panicking-code -i ./testdata/mastosan-pre-snip.wasm ./testdata/mastosan.wasm +// rm ./testdata/mastosan-pre-snip.wasm +// +// This adds about two megabytes to the resulting binary, including the AOT +// WebAssembly runtime wazero: https://wazero.io/ +package mastosan + +import ( + "bytes" + "context" + _ "embed" + "log" + "strings" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +//go:embed testdata/mastosan.wasm +var mastosanWasm []byte + +// HTML2Slackdown converts a string full of HTML text to slack-flavored markdown. +// +// Internally this works by taking that HTML and piping it to a small Rust program +// using lol_html to parse the HTML and rejigger it into slack-flavored markdown. +// This has an added latency of about 0.2 seconds per invocation, but this is as +// fast as I can make it for now. +func HTML2Slackdown(ctx context.Context, text string) (string, error) { + r := wazero.NewRuntime(ctx) + defer r.Close(ctx) + + fout := &bytes.Buffer{} + fin := bytes.NewBufferString(text) + + config := wazero.NewModuleConfig().WithStdout(fout).WithStdin(fin).WithArgs("mastosan") + + wasi_snapshot_preview1.MustInstantiate(ctx, r) + + code, err := r.CompileModule(ctx, mastosanWasm) + if err != nil { + log.Panicln(err) + } + + if _, err = r.InstantiateModule(ctx, code, config); err != nil { + return "", err + } + + return strings.TrimSpace(fout.String()), nil +} diff --git a/web/mastosan/mastosan_test.go b/web/mastosan/mastosan_test.go new file mode 100644 index 0000000..efdec88 --- /dev/null +++ b/web/mastosan/mastosan_test.go @@ -0,0 +1,32 @@ +package mastosan + +import ( + "context" + "testing" + "time" +) + +func TestHTML2Slackdown(t *testing.T) { + for _, tt := range []struct { + name string + inp, out string + }{ + {"basic mention", `<p>test mention <span class="h-card"><a href="https://vt.social/@xe" class="u-url mention">@<span>xe</span></a></span> so I can see what HTML mastodon makes</p>`, "test mention <https://vt.social/@xe|@xe> so I can see what HTML mastodon makes"}, + } { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + result, err := HTML2Slackdown(ctx, tt.inp) + if err != nil { + t.Fatal(err) + } + + if tt.out != result { + t.Logf("inp: %s", tt.inp) + t.Logf("out: %q", tt.out) + t.Logf("got: %q", result) + t.Fatal("output did not match what was expected") + } + }) + } +} diff --git a/web/mastosan/src/main.rs b/web/mastosan/src/main.rs new file mode 100644 index 0000000..7535129 --- /dev/null +++ b/web/mastosan/src/main.rs @@ -0,0 +1,48 @@ +use lol_html::{element, html_content::ContentType, HtmlRewriter, Settings}; +use std::io::{self, prelude::*, stdin, stdout}; + +fn main() -> io::Result<()> { + let mut output = Vec::new(); + let mut rewriter = HtmlRewriter::new( + Settings { + element_content_handlers: vec![ + element!("span", |el| { + el.remove_and_keep_content(); + Ok(()) + }), + element!("p", |el| { + el.append("\n\n", ContentType::Html); + el.remove_and_keep_content(); + Ok(()) + }), + element!("br", |el| { + el.append("\n\n", ContentType::Html); + el.remove_and_keep_content(); + Ok(()) + }), + element!("a[href]", |el| { + let href = el.get_attribute("href").unwrap(); + el.prepend(&format!("<{href}|"), ContentType::Html); + el.append(">", ContentType::Html); + el.remove_and_keep_content(); + + Ok(()) + }), + ], + ..Settings::default() + }, + |c: &[u8]| output.extend_from_slice(c), + ); + + let mut input = Vec::new(); + let mut fin = stdin().lock(); + fin.read_to_end(&mut input)?; + + rewriter.write(&input).unwrap(); + rewriter.end().unwrap(); + + let mut fout = stdout().lock(); + fout.write(&output)?; + + Ok(()) +} diff --git a/web/mastosan/testdata/mastosan.wasm b/web/mastosan/testdata/mastosan.wasm Binary files differnew file mode 100644 index 0000000..59f88d0 --- /dev/null +++ b/web/mastosan/testdata/mastosan.wasm |
