summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock219
-rw-r--r--Cargo.toml4
-rw-r--r--Makefile23
-rw-r--r--README.md43
-rw-r--r--clinte.json9
-rw-r--r--src/db.rs121
-rw-r--r--src/main.rs14
-rw-r--r--src/posts.rs181
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(())
 }