diff options
-rw-r--r-- | Cargo.lock | 219 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | Makefile | 23 | ||||
-rw-r--r-- | README.md | 43 | ||||
-rw-r--r-- | clinte.json | 9 | ||||
-rw-r--r-- | src/db.rs | 121 | ||||
-rw-r--r-- | src/main.rs | 14 | ||||
-rw-r--r-- | src/posts.rs | 181 |
8 files changed, 359 insertions, 255 deletions
diff --git a/Cargo.lock b/Cargo.lock index 9b8762d..9ccdc25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] +name = "addr2line" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -34,6 +42,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "backtrace" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -87,9 +107,11 @@ version = "1.0.0" dependencies = [ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fd-lock 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -129,14 +151,34 @@ dependencies = [ ] [[package]] -name = "fallible-iterator" -version = "0.2.0" +name = "failure" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" +name = "failure_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fd-lock" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "getrandom" @@ -149,6 +191,11 @@ dependencies = [ ] [[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "hermit-abi" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -157,6 +204,11 @@ dependencies = [ ] [[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -167,20 +219,6 @@ version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "libsqlite3-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -189,19 +227,6 @@ dependencies = [ ] [[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memchr" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "num-integer" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -219,9 +244,25 @@ dependencies = [ ] [[package]] -name = "pkg-config" -version = "0.3.17" +name = "object" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "redox_syscall" @@ -239,20 +280,6 @@ dependencies = [ ] [[package]] -name = "rusqlite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "rust-argon2" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -264,6 +291,44 @@ dependencies = [ ] [[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ryu" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "simplelog" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -279,6 +344,27 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "syn" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "term" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -311,6 +397,11 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "users" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -319,11 +410,6 @@ dependencies = [ ] [[package]] -name = "vcpkg" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -348,11 +434,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum backtrace 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)" = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" @@ -363,32 +451,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" -"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -"checksum fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +"checksum failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +"checksum failure_derive 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +"checksum fd-lock 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a15bec795244d49f5ee3024bdc6c3883fb035f7f6601d4a4821c3d5d60784454" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" "checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25" -"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +"checksum object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" +"checksum proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" +"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_users 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1dc1887cbcd764cc066e2c08681a5615433ac3de9752838a9ec114613b118575" -"checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" "checksum rust-argon2 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "416f5109bdd413cec4f04c029297838e7604c993f8d1483b1d438f23bdc3eb35" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +"checksum serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +"checksum serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +"checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" "checksum simplelog 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "05a3e303ace6adb0a60a9e9e2fbc6a33e1749d1e43587e2125f7efa9c5e107c5" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum syn 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "ef781e621ee763a2a40721a8861ec519cb76966aee03bb5d00adb6a31dc1c1de" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c72f4267aea0c3ec6d07eaabea6ead7c5ddacfafc5e22bcf8d186706851fb4cf" -"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index 5ee7d98..89b966a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,11 @@ maintenance = { status = "stable" } [dependencies] chrono = "^0.4" +fd-lock = "^1.1" lazy_static = "^1.4" log = "^0.4" -rusqlite = "^0.20" +serde = { version = "^1.0", features = ["derive"] } +serde_json = "^1.0" simplelog = "^0.7" users = "^0.9" diff --git a/Makefile b/Makefile index 14fce88..4545bc7 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,9 @@ BINDIR?=$(_INSTDIR)/bin DBDIR?=$(_INSTDIR)/clinte clinte: + @printf "\n%s\n" "Checking out latest tag..." + git checkout $(git describe --tags --abbrev=0) + @printf "\n%s\n" "Building clinte. This may take a minute or two." cargo build --release @printf "\n%s\n" "...Done!" @@ -16,21 +19,33 @@ clean: .PHONY: update update: + @printf "\n%s\n" "Making sure we're on master..." + git checkout master + @printf "\n%s\n" "Updating from upstream repository..." git pull --rebase + + @printf "\n%s\n" "Checking out latest tag..." + git checkout $(git describe --tags --abbrev=0) + @printf "\n%s\n" "...Done!" .PHONY: install install: @printf "\n%s\n" "Installing clinte..." @printf "\n%s\n" "Creating directories..." - mkdir -p $(BINDIR) mkdir -p $(DBDIR) + @printf "\n%s\n" "Copying files..." install -m755 target/release/clinte $(BINDIR) - touch $(DBDIR)/clinte.db - chmod 666 $(DBDIR)/clinte.db - chmod 777 $(DBDIR) + install -m666 clinte.json $(DBDIR) + + @printf "\n%s\n" "...Done!" + +.PHONY: upgrade +upgrade: + @printf "\n%s\n" "Upgrading clinte..." + install -m755 target/release/clinte $(BINDIR) @printf "\n%s\n" "...Done!" .PHONY: test diff --git a/README.md b/README.md index c6755af..6814110 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,29 @@ # clinte [![Build Status](https://travis-ci.com/gbmor/clinte.svg?branch=master)](https://travis-ci.com/gbmor/clinte) [![codecov](https://codecov.io/gh/gbmor/clinte/branch/master/graph/badge.svg)](https://codecov.io/gh/gbmor/clinte) -Command-line community notice board. Post text-only notes for other users to see. +Command-line community notice board for public-access UNIX systems. Post text-only notes for other users to see. ## Features - Username is tagged based on the executing user - Shows the 15 most recent posts in descending order -- Able to go back and edit your own posts +- Able to edit or delete your own posts - Title <= 30 chars - Body <= 500 chars - Calls `$EDITOR` when creating or modifying the body of a post - If `$EDITOR` is unset, calls `nano` +- Stores posts in JSON +- Uses advisory locking via `flock(2)` to synchronize access to the posts file [![Screenshot](https://github.com/gbmor/clinte/blob/master/assets/clinte.png)](https://github.com/gbmor/clinte/blob/master/assets/clinte.png) ## Installation -Current build dependencies are as follows: - -- `rust >= 1.36` -- `libsqlite3-dev` - -The installation for the build deps will vary based on your OS (`Linux, BSD`) - Clone the repository and jump into the directory: ``` $ git clone git://github.com/gbmor/clinte.git ... $ cd clinte -$ git checkout $(git describe --tags --abbrev=0) ``` Run the makefile and install: @@ -42,6 +36,32 @@ $ make $ sudo make install ``` +`make` will automatically checkout the latest tag and build from there. + +## Upgrading + +**Note:** v1.0.0 used sqlite3, which presented some issues. v2.0.0 uses a json structure for posts, +as this will be safer on a multi-user system. When upgrading from v1.0.0 to v2.0.0, you won't be +able to save your posts without using a third-party tool to dump the `posts` table to json, and +manually adjusting it to fit the expected format (which can be seen in the included `clinte.json`). + +*If upgrading from v1.0.0 -> v2.0.0, do a fresh install. The following applies to upgrading when +already running at least v2.0.0* + +``` +$ make update +$ make +$ make upgrade +``` + +This will: + +* checkout `master` +* pull / rebase changes from upstream +* checkout the latest tag +* rebuild +* replace the `clinte` binary, but leave the posts file untouched. + ## Usage Issuing the program name itself will list @@ -91,5 +111,4 @@ Use this flag if something's going wrong. Additional information will be written ## Notes -`sqlite` expects the directory where the database lies to be writeable by the user. So, until I move this -to using another storage medium (maybe plain text?), keep that in mind. +The file where the posts are stored must be writeable by all users on the system. Keep this in mind. \ No newline at end of file diff --git a/clinte.json b/clinte.json new file mode 100644 index 0000000..6f1c157 --- /dev/null +++ b/clinte.json @@ -0,0 +1,9 @@ +{ + "posts": [ + { + "title": "Welcome to CLI NoTEs!", + "author": "clinte!", + "body": "Welcome to clinte! For usage, run 'clinte -h'" + } + ] +} \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index 84f209b..90ff548 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,68 +1,94 @@ -use std::time; +use fd_lock::FdLock; +use serde::{Deserialize, Serialize}; + +use std::fs; +use std::fs::File; use crate::conf; use crate::error; -const DB_PATH: &str = "/usr/local/clinte/clinte.db"; +#[cfg(test)] +pub const PATH: &str = "clinte.json"; + +#[cfg(not(test))] +pub const PATH: &str = "/usr/local/clinte/clinte.json"; -#[derive(Debug)] +#[derive(Debug, Deserialize, Serialize)] pub struct Post { - pub id: u32, pub title: String, pub author: String, pub body: String, } +#[derive(Debug, Deserialize, Serialize)] +pub struct Posts { + pub posts: Vec<Post>, +} + #[derive(Debug)] pub struct Conn { - pub conn: rusqlite::Connection, + pub conn: FdLock<std::fs::File>, } impl Conn { - pub fn init(path: &str) -> rusqlite::Connection { - let start = time::Instant::now(); - + pub fn init(path: &str) -> Self { if *conf::DEBUG { - log::info!("Connecting to database"); + log::info!("Opening clinte.json"); } - let conn = error::helper( - rusqlite::Connection::open_with_flags( - path, - rusqlite::OpenFlags::SQLITE_OPEN_FULL_MUTEX - | rusqlite::OpenFlags::SQLITE_OPEN_CREATE - | rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE, - ), - "Could not connect to DB", - ); + let file = error::helper(File::open(path), "Couldn't open clinte.json"); - error::helper( - conn.execute( - "CREATE TABLE IF NOT EXISTS posts ( - id INTEGER PRIMARY KEY NOT NULL, - title TEXT NOT NULL, - author TEXT NOT NULL, - body TEXT NOT NULL - )", - rusqlite::NO_PARAMS, - ), - "Could not initialize DB", - ); + Self { + conn: FdLock::new(file), + } + } +} +impl Posts { + pub fn get_all(path: &str) -> Self { if *conf::DEBUG { - log::info!( - "Database connection established in {}ms", - start.elapsed().as_millis() - ); + log::info!("Retrieving posts..."); } - conn + let mut db = Conn::init(path); + let _guard = error::helper(db.conn.try_lock(), "Couldn't acquire lock on clinte.json"); + let strdata = error::helper(fs::read_to_string(PATH), "Couldn't read clinte.json"); + let out: Self = error::helper(serde_json::from_str(&strdata), "Couldn't parse clinte.json"); + + out } - pub fn new() -> Self { - Conn { - conn: Conn::init(DB_PATH), - } + pub fn replace(&mut self, n: usize, post: Post) { + self.posts[n] = post; + } + + pub fn get(&self, n: usize) -> &Post { + &self.posts[n] + } + + pub fn append(&mut self, post: Post) { + self.posts.push(post); + } + + pub fn delete(&mut self, n: usize) { + self.posts.remove(n); + } + + pub fn write(&self) { + let strdata = error::helper( + serde_json::to_string_pretty(&self), + "Couldn't serialize posts", + ); + + let mut db_fd = Conn::init(PATH); + let _guard = error::helper( + db_fd.conn.try_lock(), + "Couldn't acquire lock on clinte.json", + ); + error::helper( + fs::write(PATH, &strdata), + "Couldn't write data to clinte.json", + ); } } @@ -71,10 +97,19 @@ mod tests { use super::*; #[test] - fn test_new() { - let conn = Conn::init(":memory:"); - let mut stmt = conn.prepare("SELECT * FROM POSTS").unwrap(); + fn test_init() { + let mut conn = Conn::init(PATH); + conn.conn.try_lock().unwrap(); + } + + #[test] + fn retrieve_posts_and_examine() { + let all = Posts::get_all(PATH); + assert_eq!(all.posts.len(), 1); - stmt.query_map(rusqlite::NO_PARAMS, |_| Ok(())).unwrap(); + let post = all.get(0); + assert_eq!(post.title, "Welcome to CLI NoTEs!"); + assert_eq!(post.author, "clinte!"); + assert_eq!(post.body, "Welcome to clinte! For usage, run 'clinte -h'"); } } diff --git a/src/main.rs b/src/main.rs index f6d136d..6c38431 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,17 +21,15 @@ fn main() { println!("a community notices system"); println!(); - let db = db::Conn::new(); - if *conf::DEBUG { log::info!("Startup completed in {:?}ms", start.elapsed().as_millis()); } if arg_matches.subcommand_matches("post").is_some() { log::info!("New post..."); - error::helper(posts::create(&db), "Error creating new post"); + error::helper(posts::create(), "Error creating new post"); } else if let Some(updmatch) = arg_matches.subcommand_matches("update") { - let id: u32 = if let Some(val) = updmatch.value_of("id") { + let id: usize = if let Some(val) = updmatch.value_of("id") { error::helper(val.parse(), "Couldn't parse ID") } else { 0 @@ -40,11 +38,11 @@ fn main() { log::info!("Updating post ..."); error::helper( - posts::update_handler(&db, id), + posts::update_handler(id), format!("Error updating post {}", id).as_ref(), ); } else if let Some(delmatch) = arg_matches.subcommand_matches("delete") { - let id: u32 = if let Some(val) = delmatch.value_of("id") { + let id: usize = if let Some(val) = delmatch.value_of("id") { error::helper(val.parse(), "Couldn't parse ID") } else { 0 @@ -53,10 +51,10 @@ fn main() { log::info!("Deleting post"); error::helper( - posts::delete_handler(&db, id), + posts::delete_handler(id), format!("Error deleting post {}", id).as_ref(), ); } - error::helper(posts::display(&db), "Error displaying posts"); + error::helper(posts::display(), "Error displaying posts"); } diff --git a/src/posts.rs b/src/posts.rs index c9f9307..b286469 100644 --- a/src/posts.rs +++ b/src/posts.rs @@ -5,17 +5,6 @@ use crate::ed; use crate::error; use crate::user; -// Executes the sql statement that inserts a new post -// Broken off for unit testing. -pub fn exec_new(stmt: &mut rusqlite::Statement, title: &str, body: &str) -> error::Result<()> { - stmt.execute_named(&[ - (":title", &title), - (":author", &*user::NAME), - (":body", &body), - ])?; - Ok(()) -} - // Make sure nobody encodes narsty characters // into a message to negatively affect other // users @@ -29,11 +18,7 @@ fn str_to_utf8(str: &str) -> String { } // First handler for creating a new post. -pub fn create(db: &db::Conn) -> error::Result<()> { - let mut stmt = db - .conn - .prepare("INSERT INTO posts (title, author, body) VALUES (:title, :author, :body)")?; - +pub fn create() -> error::Result<()> { println!(); println!("Title of the new post: "); @@ -55,43 +40,38 @@ pub fn create(db: &db::Conn) -> error::Result<()> { } else { &body_raw }; - let trimmed_body = body.trim(); - exec_new(&mut stmt, title, trimmed_body)?; + let user = &*user::NAME; + + let mut all = db::Posts::get_all(db::PATH); + let new = db::Post { + author: user.into(), + title: title.to_string(), + body: trimmed_body.to_string(), + }; + + all.append(new); + all.write(); println!(); Ok(()) } // Shows the most recent posts. -pub fn display(db: &db::Conn) -> error::Result<()> { - let mut stmt = db.conn.prepare("SELECT * FROM posts")?; - let out = stmt.query_map(rusqlite::NO_PARAMS, |row| { - let id: u32 = row.get(0)?; - let title: String = row.get(1)?; - let author: String = row.get(2)?; - let body: String = row.get(3)?; - Ok(db::Post { - id, - title, - author, - body, - }) - })?; +pub fn display() -> error::Result<()> { + let all = db::Posts::get_all(db::PATH); let mut postvec = Vec::new(); - out.for_each(|row| { - if let Ok(post) = row { - let newpost = format!( - "{}. {} -> by {}\n{}\n\n", - post.id, - post.title.trim(), - post.author, - post.body.trim() - ); - postvec.push(newpost); - } + all.posts.iter().enumerate().for_each(|(id, post)| { + let newpost = format!( + "{}. {} -> by {}\n{}\n\n", + id + 1, + post.title.trim(), + post.author, + post.body.trim() + ); + postvec.push(newpost); }); for (i, e) in postvec.iter().enumerate() { @@ -104,8 +84,8 @@ pub fn display(db: &db::Conn) -> error::Result<()> { } // First handler to update posts. -pub fn update_handler(db: &db::Conn, id: u32) -> error::Result<()> { - let id_num_in = if id == 0 { +pub fn update_handler(id: usize) -> error::Result<()> { + let mut id_num_in = if id == 0 { println!(); println!("ID number of your post to edit?"); let mut id_num_in = String::new(); @@ -115,31 +95,29 @@ pub fn update_handler(db: &db::Conn, id: u32) -> error::Result<()> { id }; - let mut get_stmt = db.conn.prepare("SELECT * FROM posts WHERE id = :id")?; + id_num_in -= 1; - let row = get_stmt.query_row_named(&[(":id", &id_num_in)], |row| { - let title: String = row.get(1)?; - let author = row.get(2)?; - let body = row.get(3)?; - Ok(vec![title, author, body]) - })?; + let user = &*user::NAME; + let mut all = db::Posts::get_all(db::PATH); + let post = all.get(id_num_in); - if *user::NAME != row[1] { + if *user != post.author { + println!(); + println!("Users don't match. Can't update post!"); println!(); - println!("Username mismatch - can't update_handler post!"); - return Ok(()); + std::process::exit(1); } let mut new_title = String::new(); println!("Updating post {}", id_num_in); println!(); - println!("Current Title: {}", &row[0]); + println!("Current Title: {}", post.title); println!(); println!("Enter new title:"); io::stdin().read_line(&mut new_title)?; - let body_raw = str_to_utf8(&ed::call(&row[2])); + let body_raw = str_to_utf8(&ed::call(&post.body)); let body = if body_raw.len() > 500 { &body_raw[..500] } else { @@ -148,38 +126,24 @@ pub fn update_handler(db: &db::Conn, id: u32) -> error::Result<()> { let trimmed_body = body.trim(); - update(&new_title, &trimmed_body, id_num_in, &db)?; - - println!(); - Ok(()) -} - -// Allows editing of posts - called by main::update -pub fn update(new_title: &str, new_body: &str, id_num_in: u32, db: &db::Conn) -> error::Result<()> { - let new_title = new_title.trim(); - let new_body = new_body.trim(); - - let title_stmt = format!("UPDATE posts SET title = :title WHERE id = {}", id_num_in); - let mut stmt = db.conn.prepare(&title_stmt)?; - stmt.execute_named(&[(":title", &new_title)])?; - let body_stmt = format!("UPDATE posts SET body = :body WHERE id = {}", id_num_in); - let mut stmt = db.conn.prepare(&body_stmt)?; + all.replace( + id_num_in, + db::Post { + author: user.into(), + title: new_title, + body: trimmed_body.to_string(), + }, + ); - stmt.execute_named(&[(":body", &new_body)])?; - - Ok(()) -} - -// Helper to just run a sql statement. -pub fn exec_stmt_no_params(stmt: &mut rusqlite::Statement) -> error::Result<()> { - stmt.execute(rusqlite::NO_PARAMS)?; + all.write(); + println!(); Ok(()) } // First handler to remove a post -pub fn delete_handler(db: &db::Conn, id: u32) -> error::Result<()> { - let id_num_in: u32 = if id == 0 { +pub fn delete_handler(id: usize) -> error::Result<()> { + let mut id_num_in = if id == 0 { println!(); println!("ID of the post to delete?"); let mut id_num_in = String::new(); @@ -190,53 +154,20 @@ pub fn delete_handler(db: &db::Conn, id: u32) -> error::Result<()> { id }; - let del_stmt = format!("DELETE FROM posts WHERE id = {}", id_num_in); - let get_stmt = format!("SELECT * FROM posts WHERE id = {}", id_num_in); - - let mut get_stmt = db.conn.prepare(&get_stmt)?; - let mut del_stmt = db.conn.prepare(&del_stmt)?; + id_num_in -= 1; - let user_in_post: String = get_stmt.query_row(rusqlite::NO_PARAMS, |row| row.get(2))?; + let mut all = db::Posts::get_all(db::PATH); + let post = all.get(id_num_in); - if *user::NAME != user_in_post { + if *user::NAME != post.author { println!(); - println!("Users don't match. Can't delete!"); + println!("Users don't match. Can't delete post!"); println!(); - return Ok(()); + std::process::exit(1); } - exec_stmt_no_params(&mut del_stmt)?; - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn post_new() { - let db = db::Conn::init(":memory:"); - let db = db::Conn { conn: db }; - let mut stmt = db - .conn - .prepare("INSERT INTO posts (title, author, body) VALUES (:title, :author, :body)") - .unwrap(); - - let title = String::from("TEST TITLE"); - - exec_new(&mut stmt, &title, "TEST BODY").unwrap(); - update("NEW TITLE", "TEST BODY", 1, &db).unwrap(); + all.delete(id_num_in); + all.write(); - let mut stmt = db - .conn - .prepare("SELECT * FROM posts WHERE title = :title") - .unwrap(); - - let title = String::from("NEW TITLE"); - let out: String = stmt - .query_row_named(&[(":title", &title)], |row| row.get::<usize, String>(1)) - .unwrap(); - - assert_eq!("NEW TITLE", &out); - } + Ok(()) } |