diff options
Diffstat (limited to 'web/atward')
-rw-r--r-- | web/atward/.gitignore | 3 | ||||
-rw-r--r-- | web/atward/Cargo.lock | 668 | ||||
-rw-r--r-- | web/atward/Cargo.toml | 9 | ||||
-rw-r--r-- | web/atward/build.rs | 55 | ||||
-rw-r--r-- | web/atward/default.nix | 8 | ||||
-rw-r--r-- | web/atward/indexHtml/default.nix | 99 | ||||
-rw-r--r-- | web/atward/src/main.rs | 200 | ||||
-rw-r--r-- | web/atward/src/opensearch.xml | 8 | ||||
-rw-r--r-- | web/atward/src/tests.rs | 187 |
9 files changed, 1237 insertions, 0 deletions
diff --git a/web/atward/.gitignore b/web/atward/.gitignore new file mode 100644 index 000000000000..29e65519ba35 --- /dev/null +++ b/web/atward/.gitignore @@ -0,0 +1,3 @@ +result +/target +**/*.rs.bk diff --git a/web/atward/Cargo.lock b/web/atward/Cargo.lock new file mode 100644 index 000000000000..b18f55faa157 --- /dev/null +++ b/web/atward/Cargo.lock @@ -0,0 +1,668 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "ascii" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109" + +[[package]] +name = "atward" +version = "0.1.0" +dependencies = [ + "regex", + "rouille", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "chunked_transfer" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deflate" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f95bf05dffba6e6cce8dfbb30def788154949ccd9aed761b472119c21e01c70" +dependencies = [ + "adler32", + "gzip-header", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "filetime" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gzip-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0131feb3d3bb2a5a238d8a4d09f6353b7ebfdc52e77bccbf4ea6eaa751dde639" +dependencies = [ + "crc32fast", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "httparse" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "libc" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error", + "rand", + "safemem", + "tempfile", + "twoway", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" +dependencies = [ + "libc", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rouille" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18b2380c42510ef4a28b5f228a174c801e0dec590103e215e60812e2e2f34d05" +dependencies = [ + "base64", + "brotli", + "chrono", + "deflate", + "filetime", + "multipart", + "num_cpus", + "percent-encoding", + "rand", + "serde", + "serde_derive", + "serde_json", + "sha1", + "threadpool", + "time", + "tiny_http", + "url", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +dependencies = [ + "libc", + "num_threads", +] + +[[package]] +name = "tiny_http" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce51b50006056f590c9b7c3808c3bd70f0d1101666629713866c227d6e58d39" +dependencies = [ + "ascii", + "chrono", + "chunked_transfer", + "log", + "url", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[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.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/web/atward/Cargo.toml b/web/atward/Cargo.toml new file mode 100644 index 000000000000..2ecd10f96f68 --- /dev/null +++ b/web/atward/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "atward" +version = "0.1.0" +authors = ["Vincent Ambo <mail@tazj.in>"] +edition = "2018" + +[dependencies] +regex = "1.5" +rouille = "3.5" diff --git a/web/atward/build.rs b/web/atward/build.rs new file mode 100644 index 000000000000..90a2f35cd1a9 --- /dev/null +++ b/web/atward/build.rs @@ -0,0 +1,55 @@ +//! Build script that can be used outside of Nix builds to inject the +//! ATWARD_INDEX_HTML variable when building in development mode. +//! +//! Note that this script assumes that atward is in a checkout of the +//! TVL depot. + +use std::process::Command; + +static ATWARD_INDEX_HTML: &str = "ATWARD_INDEX_HTML"; +static ERROR_MESSAGE: &str = r#"Failed to build index page. + +When building during development, atward expects to be in a checkout +of the TVL depot. This is required to automatically build the index +page that is needed at compile time. + +As atward can not automatically detect the location of the page, +you must set the `ATWARD_INDEX_HTML` environment variable to the +right path. + +The expected page is build using the files in //web/atward/indexHtml +in the depot."#; + +fn main() { + // Do nothing if the variable is already set (e.g. via Nix) + if let Ok(_) = std::env::var(ATWARD_INDEX_HTML) { + return; + } + + // Otherwise ask Nix to build it and inject the result. + let output = Command::new("nix-build") + .arg("-A") + .arg("web.atward.indexHtml") + // ... assuming atward is at //web/atward ... + .arg("../..") + .output() + .expect(ERROR_MESSAGE); + + if !output.status.success() { + eprintln!( + "{}\nNix output: {}", + ERROR_MESSAGE, + String::from_utf8_lossy(&output.stderr) + ); + return; + } + + let out_path = String::from_utf8(output.stdout) + .expect("Nix returned invalid output after building index page"); + + // Return an instruction to Cargo that will set the environment + // variable during rustc calls. + // + // https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-envvarvalue + println!("cargo:rustc-env={}={}", ATWARD_INDEX_HTML, out_path.trim()); +} diff --git a/web/atward/default.nix b/web/atward/default.nix new file mode 100644 index 000000000000..f3ed04345c32 --- /dev/null +++ b/web/atward/default.nix @@ -0,0 +1,8 @@ +{ depot, ... }: + +depot.third_party.naersk.buildPackage { + src = ./.; + override = x: { + ATWARD_INDEX_HTML = depot.web.atward.indexHtml; + }; +} diff --git a/web/atward/indexHtml/default.nix b/web/atward/indexHtml/default.nix new file mode 100644 index 000000000000..3af808b89831 --- /dev/null +++ b/web/atward/indexHtml/default.nix @@ -0,0 +1,99 @@ +{ depot, ... }: + +depot.web.tvl.template { + title = "atward"; + content = '' + atward + ====== + + ---------- + + **atward** is [TVL's](https://tvl.fyi/) search + service. It can be configured as a browser search engine for easy + access to TVL bugs, code reviews, code paths and more. + + ### Setting up atward + + To configure atward, add a search engine to your browser with the + following search string: `https://at.tvl.fyi/?q=%s` + Consider setting a shortcut, for example **t** or **tvl**. + You can now quickly access TVL resources by typing something + like <kbd>t b/42</kbd> in your URL bar to get to the bug with ID + 42. + + + ### Supported queries + + The following query types are supported in atward: + + * <kbd>b/42</kbd> - access bugs with ID 42 + * <kbd>cl/3087</kbd> - access changelist with ID 3087 + * <kbd>//web/atward</kbd> - open the **//web/atward** path in TVLs monorepo + * <kbd>r/3002</kbd> - access revision 3002 in cgit + + When given a short host name (e.g. <kbd>todo</kbd> or + <kbd>cl</kbd>), atward will redirect to the appropriate `tvl.fyi` + domain. + + ### Configuration + + Some behaviour of atward can be configured by adding query + parameters to the search string: + + * <kbd>cs=true</kbd> - use Sourcegraph instead of cgit to view code + + + In some browsers (like Firefox) users can not edit query + parameters for search engines. As an alternative configuration can + be supplied via cookies with the same names as the configuration + parameters. + + The form below can set this configuration: + <form class="cheddar-callout cheddar-todo"> + <input type="checkbox" + id="cs-setting" + name="cs-setting" + onchange="saveSetting(this, 'cs');"> + <label for="cs-setting">Use Sourcegraph instead of cgit</label> + </form> + + <noscript> + <p class="cheddar-callout cheddar-warning"> + The form above only works with Javascript enabled. Only a few + lines of Javascript are used, and they are licensed under a + free-software license (MIT). + </p> + </noscript> + + ### Source code + + atward's source code lives at + [//web/atward](https://at.tvl.fyi/?q=%2F%2Fweb%2Fatward). + ''; + extraHead = '' + <script> + /* Initialise the state of all settings. */ + function loadSettings() { + loadSetting(document.getElementById('cs-setting'), 'cs'); + } + + /* Initialise the state of a setting from a cookie. */ + function loadSetting(checkbox, name) { + if (document.cookie.split(';').some(function(cookie) { + return cookie.indexOf(`''${name}=true`) >= 0; + })) { + checkbox.checked = true; + } + } + + /* Persist the state of a checkbox in a cookie */ + function saveSetting(checkbox, name) { + console.log(`setting atward parameter '''''${name}' to ''${checkbox.checked.toString()}`); + document.cookie = `''${name}=''${checkbox.checked.toString()};`; + } + + document.addEventListener('DOMContentLoaded', loadSettings); + </script> + <link rel="search" type="application/opensearchdescription+xml" title="TVL Search" href="https://at.tvl.fyi/opensearch.xml"> + ''; +} diff --git a/web/atward/src/main.rs b/web/atward/src/main.rs new file mode 100644 index 000000000000..eb2603a226c6 --- /dev/null +++ b/web/atward/src/main.rs @@ -0,0 +1,200 @@ +//! Atward implements TVL's redirection service, living at +//! atward.tvl.fyi +//! +//! This service is designed to be added as a search engine to web +//! browsers and attempts to send users to useful locations based on +//! their search query (falling back to another search engine). +use regex::Regex; +use rouille::input::cookies; +use rouille::{Request, Response}; + +#[cfg(test)] +mod tests; + +/// A query handler supported by atward. It consists of a pattern on +/// which to match and trigger the query, and a function to execute +/// that returns the target URL. +struct Handler { + /// Regular expression on which to match the query string. + pattern: Regex, + + /// Function to construct the target URL. If the pattern matches, + /// this is invoked with the captured matches and the entire URI. + /// + /// Returning `None` causes atward to fall through to the next + /// query (and eventually to the default search engine). + target: for<'s> fn(&Query, regex::Captures<'s>) -> Option<String>, +} + +/// An Atward query supplied by a user. +#[derive(Debug, PartialEq)] +struct Query { + /// Query string itself. + query: String, + + /// Should Sourcegraph be used instead of cgit? + cs: bool, +} + +/// Helper function for setting a parameter based on a query +/// parameter. +fn query_setting(req: &Request, config: &mut bool, param: &str) { + match req.get_param(param) { + Some(s) if s == "true" => *config = true, + Some(s) if s == "false" => *config = false, + _ => {} + } +} + +impl Query { + fn from_request(req: &Request) -> Option<Query> { + // First extract the actual search query ... + let mut query = match req.get_param("q") { + Some(query) => Query { query, cs: false }, + None => return None, + }; + + // ... then apply settings to it. Settings in query parameters + // take precedence over cookies. + for cookie in cookies(req) { + match cookie { + ("cs", "true") => { + query.cs = true; + } + _ => {} + } + } + + query_setting(req, &mut query.cs, "cs"); + + Some(query) + } +} + +#[cfg(test)] +impl From<&str> for Query { + fn from(query: &str) -> Query { + Query { + query: query.to_string(), + cs: false, + } + } +} + +/// Create a URL to a file (and, optionally, specific line) in cgit. +fn cgit_url(path: &str) -> String { + if path.ends_with(".md") { + format!("https://code.tvl.fyi/about/{}", path) + } else { + format!("https://code.tvl.fyi/tree/{}", path) + } +} + +/// Create a URL to a path in Sourcegraph. +fn sourcegraph_path_url(path: &str) -> String { + format!("https://cs.tvl.fyi/depot/-/tree/{}", path) +} +/// Definition of all supported query handlers in atward. +fn handlers() -> Vec<Handler> { + vec![ + // Bug IDs (e.g. b/123) + Handler { + pattern: Regex::new("^b/(?P<bug>\\d+)$").unwrap(), + target: |_, captures| Some(format!("https://b.tvl.fyi/{}", &captures["bug"])), + }, + // Changelists (e.g. cl/42) + Handler { + pattern: Regex::new("^cl/(?P<cl>\\d+)$").unwrap(), + target: |_, captures| Some(format!("https://cl.tvl.fyi/{}", &captures["cl"])), + }, + // Non-parameterised short hostnames should redirect to $host.tvl.fyi + Handler { + pattern: Regex::new("^(?P<host>b|cl|cs|code|at|todo)$").unwrap(), + target: |_, captures| Some(format!("https://{}.tvl.fyi/", &captures["host"])), + }, + // Depot revisions (e.g. r/3002) + Handler { + pattern: Regex::new("^r/(?P<rev>\\d+)$").unwrap(), + target: |_, captures| { + Some(format!( + "https://code.tvl.fyi/commit/?id=refs/r/{}", + &captures["rev"] + )) + }, + }, + // Depot paths (e.g. //web/atward or //ops/nixos/whitby/default.nix) + // TODO(tazjin): Add support for specifying lines in a query parameter + Handler { + pattern: Regex::new("^//(?P<path>[a-zA-Z].*)?$").unwrap(), + target: |query, captures| { + // Pass an empty string if the path is missing, to + // redirect to the depot root. + let path = captures.name("path").map(|m| m.as_str()).unwrap_or(""); + + if query.cs { + Some(sourcegraph_path_url(path)) + } else { + Some(cgit_url(path)) + } + }, + }, + ] +} + +/// Attempt to match against all known query types, and return the +/// destination URL if one is found. +fn dispatch(handlers: &[Handler], query: &Query) -> Option<String> { + for handler in handlers { + if let Some(captures) = handler.pattern.captures(&query.query) { + if let Some(destination) = (handler.target)(query, captures) { + return Some(destination); + } + } + } + + None +} + +/// Return the opensearch.xml file which is required for adding atward +/// as a search engine in Firefox. +fn opensearch() -> Response { + Response::text(include_str!("opensearch.xml")) + .with_unique_header("Content-Type", "application/opensearchdescription+xml") +} + +/// Render the atward index page which gives users some information +/// about how to use the service. +fn index() -> Response { + Response::html(include_str!(env!("ATWARD_INDEX_HTML"))) +} + +/// Render the fallback page which informs users that their query is +/// unsupported. +fn fallback() -> Response { + Response::text("error for emphasis that i am angery and the query whimchst i angery atward") + .with_status_code(404) +} + +fn main() { + let queries = handlers(); + let address = std::env::var("ATWARD_LISTEN_ADDRESS") + .expect("ATWARD_LISTEN_ADDRESS environment variable must be set"); + + rouille::start_server(&address, move |request| { + rouille::log(&request, std::io::stderr(), || { + if request.url() == "/opensearch.xml" { + return opensearch(); + } + + let query = match Query::from_request(&request) { + Some(q) => q, + None => return index(), + }; + + match dispatch(&queries, &query) { + None => fallback(), + Some(destination) => Response::redirect_303(destination), + } + }) + }); +} diff --git a/web/atward/src/opensearch.xml b/web/atward/src/opensearch.xml new file mode 100644 index 000000000000..6033987f5b4b --- /dev/null +++ b/web/atward/src/opensearch.xml @@ -0,0 +1,8 @@ +<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> + <ShortName>TVL</ShortName> + <Description>The Virus Lounge Search</Description> + <InputEncoding>UTF-8</InputEncoding> + <Url type="text/html" template="https://at.tvl.fyi/"> + <Param name="q" value="{searchTerms}"/> + </Url> +</OpenSearchDescription> diff --git a/web/atward/src/tests.rs b/web/atward/src/tests.rs new file mode 100644 index 000000000000..a23f96ee9a74 --- /dev/null +++ b/web/atward/src/tests.rs @@ -0,0 +1,187 @@ +use super::*; + +#[test] +fn bug_query() { + assert_eq!( + dispatch(&handlers(), &"b/42".into()), + Some("https://b.tvl.fyi/42".to_string()) + ); + + assert_eq!( + dispatch(&handlers(), &"something only mentioning b/42".into()), + None, + ); + assert_eq!(dispatch(&handlers(), &"b/invalid".into()), None,); +} + +#[test] +fn cl_query() { + assert_eq!( + dispatch(&handlers(), &"cl/42".into()), + Some("https://cl.tvl.fyi/42".to_string()) + ); + + assert_eq!( + dispatch(&handlers(), &"something only mentioning cl/42".into()), + None, + ); + assert_eq!(dispatch(&handlers(), &"cl/invalid".into()), None,); +} + +#[test] +fn depot_path_cgit_query() { + assert_eq!( + dispatch(&handlers(), &"//web/atward/default.nix".into()), + Some("https://code.tvl.fyi/tree/web/atward/default.nix".to_string()), + ); + + assert_eq!( + dispatch(&handlers(), &"//nix/readTree/README.md".into()), + Some("https://code.tvl.fyi/about/nix/readTree/README.md".to_string()), + ); + + assert_eq!(dispatch(&handlers(), &"/not/a/depot/path".into()), None); +} + +#[test] +fn depot_path_sourcegraph_query() { + assert_eq!( + dispatch( + &handlers(), + &Query { + query: "//web/atward/default.nix".to_string(), + cs: true, + } + ), + Some("https://cs.tvl.fyi/depot/-/tree/web/atward/default.nix".to_string()), + ); + + assert_eq!( + dispatch( + &handlers(), + &Query { + query: "/not/a/depot/path".to_string(), + cs: true, + } + ), + None + ); +} + +#[test] +fn depot_root_cgit_query() { + assert_eq!( + dispatch( + &handlers(), + &Query { + query: "//".to_string(), + cs: false, + } + ), + Some("https://code.tvl.fyi/tree/".to_string()), + ); +} + +#[test] +fn plain_host_queries() { + assert_eq!( + dispatch(&handlers(), &"cs".into()), + Some("https://cs.tvl.fyi/".to_string()), + ); + + assert_eq!( + dispatch(&handlers(), &"cl".into()), + Some("https://cl.tvl.fyi/".to_string()), + ); + + assert_eq!( + dispatch(&handlers(), &"b".into()), + Some("https://b.tvl.fyi/".to_string()), + ); + + assert_eq!( + dispatch(&handlers(), &"todo".into()), + Some("https://todo.tvl.fyi/".to_string()), + ); +} + +#[test] +fn request_to_query() { + assert_eq!( + Query::from_request(&Request::fake_http("GET", "/?q=b%2F42", vec![], vec![])) + .expect("request should parse to a query"), + Query { + query: "b/42".to_string(), + cs: false, + }, + ); + + assert_eq!( + Query::from_request(&Request::fake_http("GET", "/", vec![], vec![])), + None + ); +} + +#[test] +fn settings_from_cookie() { + assert_eq!( + Query::from_request(&Request::fake_http( + "GET", + "/?q=b%2F42", + vec![("Cookie".to_string(), "cs=true;".to_string())], + vec![] + )) + .expect("request should parse to a query"), + Query { + query: "b/42".to_string(), + cs: true, + }, + ); +} + +#[test] +fn settings_from_query_parameter() { + assert_eq!( + Query::from_request(&Request::fake_http( + "GET", + "/?q=b%2F42&cs=true", + vec![], + vec![] + )) + .expect("request should parse to a query"), + Query { + query: "b/42".to_string(), + cs: true, + }, + ); + + // Query parameter should override cookie + assert_eq!( + Query::from_request(&Request::fake_http( + "GET", + "/?q=b%2F42&cs=false", + vec![("Cookie".to_string(), "cs=true;".to_string())], + vec![] + )) + .expect("request should parse to a query"), + Query { + query: "b/42".to_string(), + cs: false, + }, + ); +} + +#[test] +fn depot_revision_query() { + assert_eq!( + dispatch(&handlers(), &"r/3002".into()), + Some("https://code.tvl.fyi/commit/?id=refs/r/3002".to_string()) + ); + + assert_eq!( + dispatch(&handlers(), &"something only mentioning r/3002".into()), + None, + ); + + assert_eq!(dispatch(&handlers(), &"r/invalid".into()), None,); +} |