commit 38dd291c4333b42af319315e49ec0991612ad443 Author: Avril Date: Wed Sep 9 02:20:01 2020 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2a3069 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +*~ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..aecdda4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,603 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + +[[package]] +name = "async-trait" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687c230d85c0a52504709705fc8a53e4a692b83a2184f03dae73e38e1e93a783" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "cryptohelpers" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8689d930e1c3d1cbd99014c9f7bf1a1197db8e0665ff134517b86c78233ef4fb" +dependencies = [ + "libc", + "sha2", + "tokio", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hermit-abi" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "net2" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" + +[[package]] +name = "proc-macro2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[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", +] + +[[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 = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sha2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "syn" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "tokio" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +dependencies = [ + "rand", +] + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[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.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[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-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[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" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "yuurei" +version = "0.1.0" +dependencies = [ + "async-trait", + "chrono", + "cryptohelpers", + "once_cell", + "rustc_version", + "tokio", + "uuid", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..63b101e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "yuurei" +version = "0.1.0" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = {version = "0.2", features=["full"]} +async-trait = "0.1.40" +chrono = "0.4.15" +uuid = {version = "0.8", features=["v4"]} +once_cell = "1.4.1" +crypto = {package= "cryptohelpers", version = "0.1", features= ["async", "sha256"]} + +[build-dependencies] +rustc_version = "0.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6399463 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=stable"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=beta"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=nightly"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=dev"); + } + } +} diff --git a/src/bytes.rs b/src/bytes.rs new file mode 100644 index 0000000..90177d4 --- /dev/null +++ b/src/bytes.rs @@ -0,0 +1,26 @@ + + +pub unsafe fn refer<'a, T>(src: &'a T) -> &'a [u8] + where T: ?Sized +{ + std::slice::from_raw_parts(src as *const T as *const u8, std::mem::size_of_val(src)) +} + +pub unsafe fn refer_mut<'a, T>(src: &'a mut T) -> &'a mut [u8] + where T: ?Sized +{ + std::slice::from_raw_parts_mut(src as *mut T as *mut u8, std::mem::size_of_val(src)) +} + +pub unsafe fn derefer<'a, T>(src: &'a [u8]) -> &'a T +{ + assert!(src.len() >= std::mem::size_of::()); + &*(&src[0] as *const u8 as *const T) +} + +pub unsafe fn derefer_mut<'a, T>(src: &'a mut [u8]) -> &'a mut T +{ + assert!(src.len() >= std::mem::size_of::()); + &mut *(&mut src[0] as *mut u8 as *mut T) +} + diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..87061d8 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,40 @@ +use super::*; + +//TODO: Use tokio Watcher instead, to allow hotreloading? +use once_cell::sync::OnceCell; + +static INSTANCE: OnceCell = OnceCell::new(); + +/// Set the global config instance. +/// This can only be done once for the duration of the program's runtime +pub fn set(conf: Config) +{ + INSTANCE.set(conf).expect("Failed to set global config state: Already set"); +} + +/// Get the global config instance. +/// `set()` must have been previously called or this function panics. +pub fn get() -> &'static Config +{ + INSTANCE.get().expect("Global config tried to be accessed before it was set") +} + +/// Config for this run +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Config +{ + /// Name for nanashi + pub anon_name: String, +} + +impl Default for Config +{ + #[inline] + fn default() -> Self + { + Self { + anon_name: "Nanashi", + } + } +} + diff --git a/src/identity.rs b/src/identity.rs new file mode 100644 index 0000000..0a85b93 --- /dev/null +++ b/src/identity.rs @@ -0,0 +1,64 @@ +//! Handles post and user identity. +use super::*; +use std::{ + fmt, +}; +use uuid::Uuid; + +/// A globally unique post identifier. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct PostID(Uuid); + +impl PostID +{ + /// Generate a new `PostID`. + pub fn new() -> Self + { + Self(Uuid::new_v4()) + } +} + +impl fmt::Display for PostID +{ + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + self.0.fmt(f) //TODO: Change to use kana-hash. + } +} + +/// A user's data, if set. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct User +{ + pub name: Option, + pub email: Option, + pub tripcode: Option, +} + +impl User +{ + /// The name string of this instance, or the default name. + pub fn name_str(&self) -> &str + { + self.name.as_ref().map(|x| &x[..]).unwrap_or(&config::get().anon_name[..]) + } + + /// The email string of this instance, if it is set + pub fn email_str(&self) -> Option<&str> + { + self.email.as_ref().map(|x| &x[..]) + } + + // No str for tripcode because it's not just a string (yet) + + /// Anon with no detials. + pub const fn anon() -> Self + { + Self { + name: None, + email: None, + tripcode: None, + } + } +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f98bcdc --- /dev/null +++ b/src/main.rs @@ -0,0 +1,19 @@ +#![cfg_attr(nightly, feature(option_unwrap_none))] +#![cfg_attr(nightly, feature(never_type))] + +#![allow(dead_code)] + +use async_trait::async_trait; + +mod bytes; +mod suspend; + +mod config; +mod tripcode; +mod identity; +mod post; +mod state; + +fn main() { + println!("Hello, world!"); +} diff --git a/src/post/mod.rs b/src/post/mod.rs new file mode 100644 index 0000000..5ad4b12 --- /dev/null +++ b/src/post/mod.rs @@ -0,0 +1,64 @@ +//! Structure for updating posts in-place +use super::*; +use std::{ + fmt, + hash::{ + Hash,Hasher, + }, +}; +use chrono::prelude::*; + +/// Represents all valid timestamps for a post. +/// +/// Usually in UTC, pls keep it in Utc... +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PostTimestamp +where T: TimeZone, +{ + pub opened: DateTime, + pub closed: Option>, + pub last_edit: DateTime, +} + +impl Hash for PostTimestamp { + fn hash(&self, state: &mut H) { + self.opened.hash(state); + self.closed.hash(state); + self.last_edit.hash(state); + } +} + +impl fmt::Display for PostTimestamp +where T: TimeZone, + T::Offset: fmt::Display +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + writeln!(f, " Opened - {}", self.opened)?; + writeln!(f, " Edited - {}", self.last_edit)?; + write!(f, " Closed - ")?; + if let Some(closed) = &self.closed { + writeln!(f, "{}", closed)?; + } else { + writeln!(f, "Stil open")?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// A closed and finished post. The inverse of `Imouto`. +pub struct Static +{ + id: identity::PostID, + + user: identity::User, + + title: Option, + karada: String, + + timestamp: PostTimestamp, + + /// Hash of the rest of the post data. . . . + hash: crypto::sha256::Sha256Hash, +} diff --git a/src/state/global.rs b/src/state/global.rs new file mode 100644 index 0000000..e119b35 --- /dev/null +++ b/src/state/global.rs @@ -0,0 +1,8 @@ +//! Container state for all posts +use super::*; + +/// Contains all posts in an instance, open or closed. +pub struct Oneesan +{ + +} diff --git a/src/state/local.rs b/src/state/local.rs new file mode 100644 index 0000000..1dc51a1 --- /dev/null +++ b/src/state/local.rs @@ -0,0 +1,91 @@ +//! Handles updating posts +use super::*; +use tokio::{ + sync::{ + RwLock, + watch, + }, +}; + +const MAX_SINGLE_DELTA_SIZE: usize = 16; + +/// Information about the delta to be applied in `Delta`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DeltaKind +{ + /// Append to the end of body. Equivilant to `Insert` with a location at `karada.scape.len()` (the end of the buffer). Might be removed idk + Append{ + span: [char; MAX_SINGLE_DELTA_SIZE], + span_len: u8, + }, + /// Insert `span_len` chars from `span` into body starting ahead of `location` and moving ahead + Insert{ + span: [char; MAX_SINGLE_DELTA_SIZE], + span_len: u8, + }, + /// Remove `span_len` chars ahead of `location`. (INCLUSIVE) + RemoveAhead{ + span_len: usize, + }, + /// Remove `span_len` chars behind this `location`. (EXCLUSIVE) + RemoveBehind{ + span_len: usize, + }, + /// Remove char at `location` + RemoveSingle, + /// Remove entire post body + Clear, +} + +/// A delta to apply to `Karada`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Delta +{ + /// Location to insert into. This is the INclusive range of: 0..=(karada.scape.len()). + /// + /// Insertions off the end of the buffer are to be appened instead. + location: usize, + /// The kind of delta t oinsert + kind: DeltaKind, +} + +/// Static assertion: `MAX_SINGLE_DELTA_SIZE` can fit into `u8`. +const _: [u8;(MAX_SINGLE_DELTA_SIZE < (!0u8 as usize)) as usize] = [0]; + +/// Contains post deltas and an intermediate representation of the still-open post body. +/// Created and modified with `Kokoro` worker instances. +/// +/// This should not be created by itself, instead `Kokoro` should create instances of this, so that it can retain the `watch::Sender` and other such things. +#[derive(Debug)] +pub struct Karada +{ + /// The post body so far as a vector of `char`s. + scape: RwLock>, + /// All applied deltas so far. Last applied one is at the end. + deltas: RwLock>, + + /// the latest render of the whole body string. Updated whenever a delta(s) are applied atomically. + current_body: watch::Receiver, +} + +/// Handles working on `Karada` instances. +pub struct Kokoro +{ + ...//TODO +} + +/// An open, as yet unfinied post +#[derive(Debug)] +pub struct Imouto +{ + id: identity::PostID, + + user: identity::User, + + title: Option, + karada: Karada, + + /// Hash of the current post data + hash: crypto::sha256::Sha256Hash, + timestamp: post::PostTimestamp, //Note that `closed` should always be `None` in `Imouto`. We use this for post lifetimes and such +} diff --git a/src/state/mod.rs b/src/state/mod.rs new file mode 100644 index 0000000..760a4b6 --- /dev/null +++ b/src/state/mod.rs @@ -0,0 +1,15 @@ +//! Structures for handling global and local post state. +use super::*; + +mod global; +pub use global::*; + +mod local; +pub use local::*; + +/// An open or closed post +pub enum Post +{ + Open(Imouto), + Closed(post::Static), +} diff --git a/src/suspend.rs b/src/suspend.rs new file mode 100644 index 0000000..bd0e7c1 --- /dev/null +++ b/src/suspend.rs @@ -0,0 +1,304 @@ +//! Handles suspending and reloading posts +use super::*; +use std::{ + marker::{ + PhantomData, + Unpin, + }, + io, + collections::HashMap, + ops::Range, + borrow::{ + Cow, + Borrow, + }, + error, + fmt, +}; +use tokio::{ + prelude::*, + io::{ + AsyncRead, + AsyncWrite, + }, +}; + +/// Represents an opaque bytes stream of serialised data to insert into `SuspendStream`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SuspendObject +{ + data: Vec, + data_instances: HashMap, Range>, +} + +fn calc_len(from: I) -> U +where I: IntoIterator, + T: Borrow>, + U: std::cmp::Ord + Default + Copy, +{ + let mut max = Default::default(); + + for i in from.into_iter() + { + let i = i.borrow().end; + if i > max { + max = i + } + } + max +} + +impl SuspendObject +{ + /// Create a new empty `SuspendObject`. + #[inline] pub fn new() -> Self + { + Self{data: Vec::new(), data_instances: HashMap::new()} + } + + /// Are the internal mappings of this object valid? + pub fn validate(&self) -> bool + { + let len = self.data.len(); + for (_, range) in self.data_instances.iter() { + if range.start + range.end > len { + return false; + } + } + true + } + + /// Insert bytes directly with this name + pub fn insert_bytes(&mut self, name: impl Into>, bytes: impl AsRef<[u8]>) + { + let bytes= bytes.as_ref(); + let start = self.data.len(); + self.data.extend_from_slice(bytes); + let end = self.data.len(); + + self.data_instances.insert(name.into(), start..end).unwrap_none(); + } + + /// Insert a value's bytes directly with this name + pub unsafe fn insert_value(&mut self, name: U, value: &T) + where T: ?Sized, + U: Into> + { + self.insert_bytes(name, bytes::refer(value)) + } + + /// Try to get the bytes specified by name + pub fn get_bytes(&self, name: impl Borrow) -> Option<&[u8]> + { + if let Some(range) = self.data_instances.get(name.borrow()) { + Some(&self.data[range.clone()]) + } else { + None + } + } + + /// Try to get the value spcified by name + /// + /// # Panics + /// If `T` cannot fit into the size of the range + pub unsafe fn get_value(&self, name: impl Borrow) -> Option<&T> + { + self.get_bytes(name).map(|x| bytes::derefer(x)) + } + + /// Try to get the value specified by name. Will return `None` if `T` cannot fit in the returned bytes. + pub unsafe fn try_get_value(&self, name: impl Borrow) -> Option<&T> + { + if let Some(bytes) = self.get_bytes(name) { + if bytes.len() >= std::mem::size_of::() { + Some(bytes::derefer(bytes)) + } else { + #[cfg(debug_assertions)] eprintln!("Warning! Likely data corruption as {} (size {}) cannot fit into {} bytes",std::any::type_name::(), std::mem::size_of::(), bytes.len()); + None + } + } else { + None + } + } + + /// Consume into the data + #[deprecated = "Invalid ABI"] fn into_bytes(self) -> Box<[u8]> + { + let mut output = Vec::new(); //TOOO: Get cap + + debug_assert!(self.validate(), "passing invalid object to serialise"); + + unsafe { + output.extend_from_slice(bytes::refer(&self.data_instances.len())); + for (name, Range{start, end}) in self.data_instances.into_iter() { + let name = name.as_bytes(); + + output.extend_from_slice(bytes::refer(&name.len())); + output.extend_from_slice(name); + + output.extend_from_slice(bytes::refer(&start)); + output.extend_from_slice(bytes::refer(&end)); + } + } + + output.extend(self.data); + output.into_boxed_slice() + } + + /// Read an object from a stream + pub async fn from_stream(input: &mut T) -> io::Result + where T: AsyncRead + Unpin + ?Sized + { + let mut ext_buf = Vec::new(); + + macro_rules! bin { + (usize) => { + { + use std::convert::TryFrom; + let value: u64 = input.read_u64().await?; + usize::try_from(value).map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "u64 cannot fit in usize"))? + } + }; + ($size:expr) => { + { + let sz = $size; + ext_buf.resize(sz, 0); + assert_eq!(input.read_exact(&mut ext_buf[..sz]).await?, ext_buf.len()); + &ext_buf[..sz] + } + } + } + let entries = bin!(usize); + + let mut instances = HashMap::with_capacity(entries); + + for _i in 0..entries + { + let name_len = bin!(usize); + + let name_bytes = bin!(name_len); + let name_str = std::str::from_utf8(name_bytes).map_err(|_| io::Error::new(io::ErrorKind::InvalidData,"item name was corrupted"))?; + + let start = bin!(usize); + let end = bin!(usize); + + instances.insert(Cow::Owned(name_str.to_owned()), start..end); + } + + let expected_len = bin!(usize); + if expected_len != calc_len(instances.iter().map(|(_, v)| v)) + { + return Err(io::Error::new(io::ErrorKind::InvalidData, "expected and read sizes differ")); + } + + let mut data = vec![0; expected_len]; + assert_eq!(input.read_exact(&mut data[..]).await?, expected_len); + + Ok(Self { + data, + data_instances: instances, + }) + } + + /// Write this instance into an async stream + pub async fn into_stream(&self, output: &mut T) -> io::Result + where T: AsyncWrite + Unpin + ?Sized + { + debug_assert!(self.validate(), "passing invalid object to serialise"); + + let mut written=0usize; + macro_rules! bin { + ($bytes:expr) => { + { + let bytes = $bytes; + output.write_all(bytes).await?; + written+=bytes.len(); + } + }; + (usize $value:expr) => { + { + use std::convert::TryInto; + let val: u64 = $value.try_into().map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "size cannot fit in u64"))?; + output.write_u64(val).await?; + written += std::mem::size_of::(); + } + }; + } + + unsafe { + bin!(usize self.data_instances.len()); + for (name, &Range{start, end}) in self.data_instances.iter() { + let name = name.as_bytes(); + + bin!(usize name.len()); + bin!(name); + + bin!(usize start); + bin!(usize end); + } + } + + bin!(usize self.data.len()); //for additional checks + bin!(&self.data); + Ok(written) + } +} + +/// A suspend stream represents a stream of objects of _the same type_. Can be any number of them, but they all must be for the same type. +#[async_trait] +pub trait SuspendStream +{ + /// Write an object into the opaque stream. + async fn set_object(&mut self, obj: SuspendObject) -> Result<(), SuspendError>; + /// Read an object from the opaque stream. + async fn get_object(&mut self) -> Result, SuspendError>; +} + +/// An error that occoured in a suspend operation +#[derive(Debug)] +#[non_exhaustive] +pub enum SuspendError { + BadObject, + Corruption, + IO(io::Error), + + Unknown, +} +impl error::Error for SuspendError +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match &self { + Self::IO(io) => io, + _ => return None, + }) + } +} +impl fmt::Display for SuspendError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::BadObject => write!(f, "unexpected object in stream"), + Self::Corruption => write!(f, "data stream corruption"), + Self::IO(_) => write!(f, "i/o error"), + _ => write!(f, "unknown error"), + } + } +} + + +/// A suspendable type, that can save and reload its data atomically +#[async_trait] +pub trait Suspendable: Sized +{ + async fn suspend(self, into: &mut S) -> Result<(), SuspendError>; + async fn load(from: &mut S) -> Result; +} + +/* +pub struct SuspenceState +where T: Suspendable +{ + +_phantom: PhantomData, +}*/ diff --git a/src/tripcode.rs b/src/tripcode.rs new file mode 100644 index 0000000..5ecf928 --- /dev/null +++ b/src/tripcode.rs @@ -0,0 +1,12 @@ +//! Tripcode. TODO: Use kana-hash +use super::*; + + +///TODO: A kana-hash tripcode +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub enum Tripcode +{ + Secure(i32), + Normal(i32), + Special(&'static str), +}