diff options
author | Vika <vika@fireburn.ru> | 2021-08-15 15:13:34 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2021-08-15 15:13:34 +0300 |
commit | 13bcfb013c4a5ac5ea15c7ebe04f243431165c03 (patch) | |
tree | 514619680064668bab41dcfcafb6d601f89d6806 | |
parent | c017b87aea27b61b5b3eb98d2d6cf53ea3d116b6 (diff) | |
download | kittybox-13bcfb013c4a5ac5ea15c7ebe04f243431165c03.tar.zst |
Added a WIP file backend
Currently unavailable for use and only has basic GET and POST operations implemented. A lot more work is needed to make it truly usable. Locking is implemented using flock() system call on Linux.
-rw-r--r-- | Cargo.lock | 238 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | src/database/file/mod.rs | 173 | ||||
-rw-r--r-- | src/database/mod.rs | 67 |
4 files changed, 399 insertions, 84 deletions
diff --git a/Cargo.lock b/Cargo.lock index fecebc8..b6ce993 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,11 +64,20 @@ dependencies = [ [[package]] name = "aho-corasick" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" +dependencies = [ + "memchr 0.1.11", +] + +[[package]] +name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "memchr", + "memchr 2.4.0", ] [[package]] @@ -174,7 +183,7 @@ dependencies = [ "http-types", "httparse", "lazy_static", - "log", + "log 0.4.14", "pin-project", ] @@ -187,14 +196,14 @@ dependencies = [ "concurrent-queue", "futures-lite", "libc", - "log", + "log 0.4.14", "once_cell", "parking", "polling", "slab", "socket2", "waker-fn", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -229,7 +238,7 @@ dependencies = [ "libc", "once_cell", "signal-hook", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -262,8 +271,8 @@ dependencies = [ "async-channel", "async-std", "http-types", - "log", - "memchr", + "log 0.4.14", + "memchr 2.4.0", "pin-project-lite 0.1.12", ] @@ -286,8 +295,8 @@ dependencies = [ "futures-lite", "gloo-timers", "kv-log-macro", - "log", - "memchr", + "log 0.4.14", + "memchr 2.4.0", "num_cpus", "once_cell", "pin-project-lite 0.2.7", @@ -310,7 +319,7 @@ checksum = "ba5fa6ed76cb2aa820707b4eb9ec46f42da9ce70b0eafab5e5e34942b38a44d5" dependencies = [ "libc", "wasm-bindgen", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -351,7 +360,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -494,7 +503,7 @@ dependencies = [ "num-traits", "serde", "time 0.1.44", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -514,7 +523,7 @@ checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" dependencies = [ "atty", "lazy_static", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -525,7 +534,7 @@ checksum = "a2d47c1b11006b87e492b53b313bb699ce60e16613c4dddaa91f8f7c220ab2fa" dependencies = [ "bytes", "futures-util", - "memchr", + "memchr 2.4.0", "pin-project-lite 0.2.7", "tokio", ] @@ -781,7 +790,7 @@ checksum = "18a857bc01b5ae04874234f6b5d16b8b8fa86910aa5777479c2669b5df607fce" dependencies = [ "html5ever", "kuchiki", - "regex", + "regex 1.5.4", ] [[package]] @@ -804,14 +813,24 @@ dependencies = [ [[package]] name = "env_logger" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" +dependencies = [ + "log 0.3.9", + "regex 0.1.80", +] + +[[package]] +name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "atty", "humantime", - "log", - "regex", + "log 0.4.14", + "regex 1.5.4", "termcolor", ] @@ -831,6 +850,17 @@ dependencies = [ ] [[package]] +name = "fd-lock" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8806dd91a06a7a403a8e596f9bfbfb34e469efbc363fc9c9713e79e26472e36" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + +[[package]] name = "femme" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -838,7 +868,7 @@ checksum = "2af1a24f391a5a94d756db5092c6576aad494b88a71a5a36b20c67b63e0df034" dependencies = [ "cfg-if 0.1.10", "js-sys", - "log", + "log 0.4.14", "serde", "serde_derive", "serde_json", @@ -947,7 +977,7 @@ dependencies = [ "fastrand", "futures-core", "futures-io", - "memchr", + "memchr 2.4.0", "parking", "pin-project-lite 0.2.7", "waker-fn", @@ -997,7 +1027,7 @@ dependencies = [ "futures-macro", "futures-sink", "futures-task", - "memchr", + "memchr 2.4.0", "pin-project-lite 0.2.7", "pin-utils", "proc-macro-hack", @@ -1120,7 +1150,7 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b" dependencies = [ - "log", + "log 0.4.14", "mac", "markup5ever", "proc-macro2", @@ -1143,7 +1173,7 @@ dependencies = [ "deadpool", "futures", "http-types", - "log", + "log 0.4.14", "rustls", ] @@ -1223,6 +1253,16 @@ dependencies = [ ] [[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 = "kittybox" version = "0.1.0" dependencies = [ @@ -1232,18 +1272,20 @@ dependencies = [ "chrono", "easy-scraper", "ellipse", - "env_logger", + "env_logger 0.8.4", + "fd-lock", "futures", "futures-util", "http-types", "lazy_static", - "log", + "log 0.4.14", "markdown", "markup", "mobc", "mobc-redis", "mockito", "newbase60", + "paste", "prometheus", "retainer", "serde", @@ -1251,6 +1293,7 @@ dependencies = [ "serde_urlencoded", "surf", "tempdir", + "test-logger", "tide", "tide-testing", "url", @@ -1274,7 +1317,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" dependencies = [ - "log", + "log 0.4.14", ] [[package]] @@ -1313,6 +1356,15 @@ dependencies = [ [[package]] name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.14", +] + +[[package]] +name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" @@ -1335,7 +1387,7 @@ checksum = "ef3aab6a1d529b112695f72beec5ee80e729cb45af58663ec902c8fac764ecdd" dependencies = [ "lazy_static", "pipeline", - "regex", + "regex 1.5.4", ] [[package]] @@ -1365,7 +1417,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" dependencies = [ - "log", + "log 0.4.14", "phf", "phf_codegen", "string_cache", @@ -1381,6 +1433,15 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" @@ -1423,7 +1484,7 @@ dependencies = [ "futures-core", "futures-timer", "futures-util", - "log", + "log 0.4.14", "tokio", ] @@ -1448,9 +1509,9 @@ dependencies = [ "difference", "httparse", "lazy_static", - "log", + "log 0.4.14", "rand 0.8.4", - "regex", + "regex 1.5.4", "serde_json", "serde_urlencoded", ] @@ -1480,7 +1541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "lexical-core", - "memchr", + "memchr 2.4.0", "version_check", ] @@ -1553,10 +1614,16 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "winapi", + "winapi 0.3.9", ] [[package]] +name = "paste" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" + +[[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1668,9 +1735,9 @@ checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" dependencies = [ "cfg-if 1.0.0", "libc", - "log", + "log 0.4.14", "wepoll-ffi", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1741,7 +1808,7 @@ dependencies = [ "fnv", "lazy_static", "libc", - "memchr", + "memchr 2.4.0", "parking_lot", "procfs", "protobuf", @@ -1773,7 +1840,7 @@ dependencies = [ "libc", "rand_core 0.3.1", "rdrand", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1923,17 +1990,36 @@ dependencies = [ [[package]] name = "regex" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" +dependencies = [ + "aho-corasick 0.5.3", + "memchr 0.1.11", + "regex-syntax 0.3.9", + "thread_local", + "utf8-ranges", +] + +[[package]] +name = "regex" version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "aho-corasick 0.7.18", + "memchr 2.4.0", + "regex-syntax 0.6.25", ] [[package]] name = "regex-syntax" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" + +[[package]] +name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" @@ -1944,7 +2030,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1955,7 +2041,7 @@ checksum = "59039dbf4a344af919780e9acdf7f9ce95deffb0152a72eca94b89d6a2bf66c0" dependencies = [ "async-lock", "async-timer", - "log", + "log 0.4.14", "rand 0.8.4", ] @@ -1971,7 +2057,7 @@ dependencies = [ "spin", "untrusted", "web-sys", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1996,7 +2082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" dependencies = [ "base64 0.12.3", - "log", + "log 0.4.14", "ring", "sct", "webpki", @@ -2034,7 +2120,7 @@ dependencies = [ "cssparser", "derive_more", "fxhash", - "log", + "log 0.4.14", "matches", "phf", "phf_codegen", @@ -2196,7 +2282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2319,7 +2405,7 @@ dependencies = [ "futures-util", "http-client", "http-types", - "log", + "log 0.4.14", "mime_guess", "pin-project-lite 0.2.7", "serde", @@ -2375,6 +2461,15 @@ dependencies = [ ] [[package]] +name = "test-logger" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55ec868b79cb8e63f8921843c10e3083137cfaa171a67209e6a2656ccd4d8a" +dependencies = [ + "env_logger 0.3.5", +] + +[[package]] name = "thin-slice" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2401,6 +2496,25 @@ dependencies = [ ] [[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" +dependencies = [ + "kernel32-sys", + "libc", +] + +[[package]] +name = "thread_local" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" +dependencies = [ + "thread-id", +] + +[[package]] name = "tide" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2416,7 +2530,7 @@ dependencies = [ "http-client", "http-types", "kv-log-macro", - "log", + "log 0.4.14", "pin-project-lite 0.2.7", "route-recognizer", "serde", @@ -2443,7 +2557,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2458,7 +2572,7 @@ dependencies = [ "stdweb", "time-macros", "version_check", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2507,7 +2621,7 @@ checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985" dependencies = [ "autocfg", "bytes", - "memchr", + "memchr 2.4.0", "num_cpus", "pin-project-lite 0.2.7", ] @@ -2521,7 +2635,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "log", + "log 0.4.14", "pin-project-lite 0.2.7", "tokio", ] @@ -2607,6 +2721,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" + +[[package]] name = "value-bag" version = "1.0.0-alpha.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2661,7 +2781,7 @@ checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" dependencies = [ "bumpalo", "lazy_static", - "log", + "log 0.4.14", "proc-macro2", "quote", "syn", @@ -2749,6 +2869,12 @@ dependencies = [ [[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" @@ -2758,6 +2884,12 @@ dependencies = [ ] [[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" @@ -2769,7 +2901,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 73ee6ed..3974d9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,15 @@ required-features = ["util"] tide-testing = "^0.1.3" # tide testing helper mockito = "^0.30.0" # HTTP mocking for Rust. tempdir = "^0.3.7" # A library for managing a temporary directory and deleting all contents when it's dropped +paste = "^1.0.5" # Macros for all your token pasting needs +test-logger = "^0.1.0" # Simple helper to initialize env_logger before unit and integration tests [dependencies] async-trait = "^0.1.50" # Type erasure for async trait methods easy-scraper = "^0.2.0" # HTML scraping library focused on ease of use ellipse = "^0.2.0" # Truncate and ellipsize strings in a human-friendly way env_logger = "^0.8.3" # A logging implementation for `log` which is configured via an environment variable +fd-lock = "^3.0.0" # Advisory reader-writer locks for files futures = "^0.3.14" # An implementation of futures and streams futures-util = "^0.3.14" # Common utilities and extension traits for the futures-rs library http-types = "^2.11.0" # Common types for HTTP operations @@ -47,7 +50,7 @@ version = "^1.0.42" optional = true [dependencies.async-std] # Async version of the Rust standard library version = "^1.9.0" -features = ["attributes"] +features = ["attributes", "unstable"] [dependencies.chrono] # Date and time library for Rust version = "^0.4.19" features = ["serde"] diff --git a/src/database/file/mod.rs b/src/database/file/mod.rs new file mode 100644 index 0000000..36300fc --- /dev/null +++ b/src/database/file/mod.rs @@ -0,0 +1,173 @@ +//pub mod async_file_ext; +use async_std::fs::{File, OpenOptions}; +use async_std::io::{ErrorKind as IOErrorKind, BufReader}; +use async_std::io::prelude::*; +use async_std::task::spawn_blocking; +use async_trait::async_trait; +use crate::database::{ErrorKind, Result, Storage, StorageError}; +use fd_lock::RwLock; +use log::debug; +use std::path::{Path, PathBuf}; + +impl From<std::io::Error> for StorageError { + fn from(source: std::io::Error) -> Self { + Self::with_source( + match source.kind() { + IOErrorKind::NotFound => ErrorKind::NotFound, + _ => ErrorKind::Backend, + }, + "file I/O error", + Box::new(source), + ) + } +} + +async fn get_lockable_file(file: File) -> RwLock<File> { + debug!("Trying to create a file lock"); + spawn_blocking(move || RwLock::new(file)).await +} + +fn url_to_path(root: &Path, url: &str) -> PathBuf { + let url = http_types::Url::parse(url).expect("Couldn't parse a URL"); + let mut path: PathBuf = root.to_owned(); + path.push(url.origin().ascii_serialization() + &url.path().to_string() + ".json"); + + path +} + +#[derive(Clone)] +pub struct FileStorage { + root_dir: PathBuf, +} + +impl FileStorage { + pub async fn new(root_dir: PathBuf) -> Result<Self> { + // TODO check if the dir is writable + Ok(Self { root_dir }) + } +} + +#[async_trait] +impl Storage for FileStorage { + async fn post_exists(&self, url: &str) -> Result<bool> { + let path = url_to_path(&self.root_dir, url); + debug!("Checking if {:?} exists...", path); + Ok(spawn_blocking(move || path.is_file()).await) + } + + async fn get_post(&self, url: &str) -> Result<Option<serde_json::Value>> { + let path = url_to_path(&self.root_dir, url); + debug!("Opening {:?}", path); + // We have to special-case in here because the function should return Ok(None) on 404 + match File::open(path).await { + Ok(f) => { + let lock = get_lockable_file(f).await; + let guard = lock.read()?; + + // HOW DOES THIS TYPECHECK?!!!!!!!! + // Read::read(&mut self) requires a mutable reference + // yet Read is implemented for &File + // We can't get a &mut File from &File, can we? + // And we need a &mut File to use Read::read_to_string() + // Yet if we pass it to a BufReader it works?!! + // + // I hate magic + // + // TODO find a way to get rid of BufReader here + let mut content = String::new(); + let mut reader = BufReader::new(&*guard); + reader.read_to_string(&mut content).await?; + drop(reader); + drop(guard); + Ok(Some(serde_json::from_str(&content)?)) + } + Err(err) => { + if err.kind() == IOErrorKind::NotFound { + Ok(None) + } else { + Err(err.into()) + } + } + } + } + + async fn put_post<'a>(&self, post: &'a serde_json::Value, user: &'a str) -> Result<()> { + let key = post["properties"]["uid"][0] + .as_str() + .expect("Tried to save a post without UID"); + let path = url_to_path(&self.root_dir, key); + + debug!("Creating {:?}", path); + + let parent = path.parent().unwrap().to_owned(); + if !spawn_blocking(move || parent.is_dir()).await { + async_std::fs::create_dir_all(path.parent().unwrap()).await?; + } + + let f = OpenOptions::new() + .write(true) + .create_new(true) + .open(&path) + .await?; + + let mut lock = get_lockable_file(f).await; + let mut guard = lock.write()?; + + (*guard).write(post.to_string().as_bytes()).await?; + drop(guard); + + if post["properties"]["url"].is_array() { + for url in post["properties"]["url"] + .as_array() + .unwrap() + .iter() + .map(|i| i.as_str().unwrap()) + { + // TODO consider using the symlink crate + // to promote cross-platform compat on Windows + // do we even need to support Windows?... + if url != key && url.starts_with(user) { + let link = url_to_path(&self.root_dir, url); + debug!("Creating a symlink at {:?}", link); + let orig = path.clone(); + spawn_blocking(move || { std::os::unix::fs::symlink(orig, link) }).await?; + } + } + } + + Ok(()) + } + + async fn update_post<'a>(&self, url: &'a str, update: serde_json::Value) -> Result<()> { + todo!() + } + + async fn get_channels( + &self, + user: &crate::indieauth::User, + ) -> Result<Vec<super::MicropubChannel>> { + todo!() + } + + async fn read_feed_with_limit<'a>( + &self, + url: &'a str, + after: &'a Option<String>, + limit: usize, + user: &'a Option<String>, + ) -> Result<Option<serde_json::Value>> { + todo!() + } + + async fn delete_post<'a>(&self, url: &'a str) -> Result<()> { + todo!() + } + + async fn get_setting<'a>(&self, setting: &'a str, user: &'a str) -> Result<String> { + todo!() + } + + async fn set_setting<'a>(&self, setting: &'a str, user: &'a str, value: &'a str) -> Result<()> { + todo!() + } +} diff --git a/src/database/mod.rs b/src/database/mod.rs index 7b144f8..58f0a35 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -7,6 +7,8 @@ mod redis; pub use crate::database::redis::RedisStorage; #[cfg(test)] pub use redis::tests::{get_redis_instance, RedisInstance}; +mod file; +pub use crate::database::file::FileStorage; #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct MicropubChannel { @@ -133,12 +135,6 @@ pub trait Storage: Clone + Send + Sync { /// Note that the `post` object MUST have `post["properties"]["uid"][0]` defined. async fn put_post<'a>(&self, post: &'a serde_json::Value, user: &'a str) -> Result<()>; - /*/// Save a post and add it to the relevant feeds listed in `post["properties"]["channel"]`. - /// - /// Note that the `post` object MUST have `post["properties"]["uid"][0]` defined - /// and `post["properties"]["channel"]` defined, even if it's empty. - async fn put_and_index_post<'a>(&mut self, post: &'a serde_json::Value) -> Result<()>;*/ - /// Modify a post using an update object as defined in the Micropub spec. /// /// Note to implementors: the update operation MUST be atomic OR MUST lock the database @@ -191,6 +187,7 @@ mod tests { use super::redis::tests::get_redis_instance; use super::{MicropubChannel, Storage}; use serde_json::json; + use paste::paste; async fn test_backend_basic_operations<Backend: Storage>(backend: Backend) { let post: serde_json::Value = json!({ @@ -210,7 +207,7 @@ mod tests { .put_post(&post, "https://fireburn.ru/") .await .unwrap(); - if let Ok(Some(returned_post)) = backend.get_post(&key).await { + if let Some(returned_post) = backend.get_post(&key).await.unwrap() { assert!(returned_post.is_object()); assert_eq!( returned_post["type"].as_array().unwrap().len(), @@ -300,30 +297,40 @@ mod tests { "Vika's Hideout" ); } - - #[async_std::test] - async fn test_redis_storage_basic_operations() { - let redis_instance = get_redis_instance().await; - let backend = super::RedisStorage::new(redis_instance.uri().to_string()) - .await - .unwrap(); - test_backend_basic_operations(backend).await; - } - #[async_std::test] - async fn test_redis_storage_channel_list() { - let redis_instance = get_redis_instance().await; - let backend = super::RedisStorage::new(redis_instance.uri().to_string()) - .await - .unwrap(); - test_backend_get_channel_list(backend).await; + macro_rules! redis_test { + ($func_name:expr) => { + paste! { + #[async_std::test] + async fn [<redis_ $func_name>] () { + test_logger::ensure_env_logger_initialized(); + let redis_instance = get_redis_instance().await; + let backend = super::RedisStorage::new(redis_instance.uri().to_string()) + .await + .unwrap(); + $func_name(backend).await + } + } + } } - #[async_std::test] - async fn test_redis_settings() { - let redis_instance = get_redis_instance().await; - let backend = super::RedisStorage::new(redis_instance.uri().to_string()) - .await - .unwrap(); - test_backend_settings(backend).await; + macro_rules! file_test { + ($func_name:expr) => { + paste! { + #[async_std::test] + async fn [<file_ $func_name>] () { + test_logger::ensure_env_logger_initialized(); + let tempdir = tempdir::TempDir::new("file").expect("Failed to create tempdir"); + let backend = super::FileStorage::new(tempdir.into_path()).await.unwrap(); + $func_name(backend).await + } + } + } } + + redis_test!(test_backend_basic_operations); + redis_test!(test_backend_get_channel_list); + redis_test!(test_backend_settings); + file_test!(test_backend_basic_operations); + file_test!(test_backend_get_channel_list); + file_test!(test_backend_settings); } |