commit 03ef2231082034a818c78f33b6ba0ef56c23a24c Author: Erin Nova Date: Wed Dec 29 16:04:00 2021 -0500 Initial commit uwu diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f7adcae --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,887 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "3.0.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7843ae7a539bef687e018bf9edf7e87728024b29d02b0f8409726be8880ae1a" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.0.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae3cc2f259ea636871f5da15b0ac033f1821d7a5506c3d1bfbdde201f14c803" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "confy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2913470204e9e8498a0f31f17f90a0de801ae92c8c5ac18c49af4819e6786697" +dependencies = [ + "directories", + "serde", + "toml", +] + +[[package]] +name = "console" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "crc32fast" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "dialoguer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61579ada4ec0c6031cfac3f86fdba0d195a7ebeb5e36693bd53cb5999a25beeb" +dependencies = [ + "console", + "lazy_static", + "tempfile", + "zeroize", +] + +[[package]] +name = "directories" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" +dependencies = [ + "cfg-if 0.1.10", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "exitcode" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + +[[package]] +name = "paris" +version = "1.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25b44bc50356452ab4fdcdd6d6b0f70eddc4f9fd16c9ba142f69e74fb195165" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot", + "zstd", +] + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "tinymark" +version = "0.2.0" +dependencies = [ + "bincode", + "chrono", + "clap", + "confy", + "dialoguer", + "exitcode", + "paris", + "serde", + "serde_derive", + "serde_json", + "sled", + "url", + "uuid", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", + "serde", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "serde", + "sha1", +] + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" + +[[package]] +name = "zstd" +version = "0.9.1+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538b8347df9257b7fbce37677ef7535c00a3c7bf1f81023cc328ed7fe4b41de8" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb4cfe2f6e6d35c5d27ecd9d256c4b6f7933c4895654917460ec56c29336cc1" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a342c50 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "tinymark" +description = "A tiny bookmark manager" +version = "0.2.0" +edition = "2021" +authors = ["Erin Nova "] +license = "CNPLv7+" +readme = "README.md" +homepage = "https://git.lavender.software/erin/tinymark" +repository = "https://git.lavender.software/erin/tinymark" +keywords = ["cli", "bookmark", "tiny"] +categories = ["command-line-utilities"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +opt-level = "z" # Optimize for size. +lto = true +codegen-units = 1 +panic = "abort" + +[dependencies] +url = { version = "2.2.2", features = ["serde"] } +uuid = { version = "0.8.2", features = ["v5", "serde"] } +sled = { version = "0.34.7", features = ["compression"] } +bincode = "1.3.3" +paris = { version = "1.5", features = ["macros"] } +serde = { version = "1.0", features = ["derive"] } +clap = { version = "3.0.0-rc.9", features = ["derive"] } +exitcode = "1.1.2" +serde_json = "1.0" +chrono = { version = "0.4", features = ["serde"] } +dialoguer = "0.9.0" +confy = "0.4" +serde_derive = "1.0.132" diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d6909a --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# TinyMark +A tiny bookmark manager. + +## About +This is just a small tool I've made for myself to organize my bookmarks in a simple way, +without needing a browser. + +It doesn't support icons or much of anything else right now. + +A configuration file will be stored in `~/.config/tinymark/tinymark.toml` +By default the database will be stored in `~/.local/share/tinymark` + +## Using +List stored bookmarks: +`tinymark list` + +Add a bookmark: +`tinymark add https://example.com name` + +With optional tags & description: +`tinymark add https://example.com name "test description" tags,separated,by,comma` + +Delete a previously added bookmark: +`tinymark delete ` + +You can export all the stored bookmarks to a JSON file with `tinymark export ` + +You can then import a previously exported JSON file with `tinymark import ` + +## JSON +This program can output in JSON format if you supply it with the `--json` argument. + +Any command that returns a Bookmark will return a JSON serialized object of it. + +Otherwise it will return a 'status' key of value 'success' or 'fail', +along with a 'reason' value with a full-length output. diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..6e09b8f --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,171 @@ +use url::Url; +use std::env::VarError; +use chrono::Utc; +use crate::structures::Bookmark; +use paris::*; +use crate::database; +use serde_json::json; +use std::path::PathBuf; +use std::fs::File; +use std::io::{BufReader, BufWriter}; + +use dialoguer::{ + Select, + theme::ColorfulTheme, + console::Term +}; + +pub fn edit(json: bool, url: &Option, path: Option) { + if json { + println!("{}", json!({ + "status": "fail", + "reason": "unsupported command", + })); + std::process::exit(exitcode::DATAERR); + } else { + match url { + Some(link) => { + println!("User selected item :\n{}", link); + }, + None => { + match database::get_all(json, path) { + Some(bookmarks) => { + let mut items: Vec<&Url> = Vec::new(); + for i in &bookmarks { + items.push(&i.link); + } + let selection = Select::with_theme(&ColorfulTheme::default()) + .items(&items) + .default(0) + .interact_on_opt(&Term::stderr()).unwrap(); + + match selection { + Some(index) => println!("User selected item :\n{}", bookmarks[index]), + None => println!("User did not select anything") + } + }, + None => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": "an error ocurred in running your command", + })); + } else { + warn!("an error ocurred in running your command"); + } + }, + } + }, + } + } +} + +pub fn add(url: &Url, name: &String, description: &Option, tags: &Vec, json: bool, path: Option) { + let bookmark = Bookmark { + link: url.to_owned(), + label: name.to_string(), + description: description.to_owned(), + tags: tags.to_vec(), + container: None, + created_at: Utc::now(), + }; + + database::insert_entry(&bookmark, json, path); + if json { + println!("{}", serde_json::to_string(&bookmark).unwrap()); + } else { + println!("Added new bookmark!"); + println!("{}", bookmark); + }; +} + +pub fn list(json: bool, path: Option) { + match database::get_all(json, path) { + Some(bookmarks) => { + for i in bookmarks { + if json { + println!("{}", serde_json::to_string(&i).unwrap()); + } else { + println!("{}", i); + } + } + }, + None => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": "an error ocurred in running your command", + })); + } else { + warn!("an error ocurred in running your command"); + } + } + } +} + +pub fn env_err(json: bool, e: VarError) { + if json { + println!("{}", json!({ + "status": "fail", + "reason": e.to_string(), + })); + } else { + warn!("couldn't read $HOME environment variable: {}", e); + } +} + +pub fn export(file_path: PathBuf, json: bool, path: Option) { + if json { + println!("{}", json!({ + "status": "fail", + "reason": "unsupported command", + })); + } else { + let file = match File::create(&file_path) { + Ok(f) => f, + Err(e) => { + warn!("error opening file! {}", e); + std::process::exit(exitcode::DATAERR); + }, + }; + let writer = BufWriter::new(file); + + match database::get_all(json, path) { + Some(bookmarks) => serde_json::to_writer(writer, &bookmarks).unwrap(), + None => std::process::exit(exitcode::IOERR), + } + info!("Succesfully exported bookmarks to {}!", file_path.to_str().unwrap()); + } +} + +pub fn import(file_path: PathBuf, json: bool) { + match json { + true => { + println!("{}", json!({ + "status": "fail", + "reason": "unsupported command", + })); + }, + false => { + let file = match File::open(&file_path) { + Ok(f) => f, + Err(e) => { + warn!("error opening file! {}", e); + std::process::exit(exitcode::DATAERR); + } + }; + let reader = BufReader::new(file); + + let bookmarks: Vec = match serde_json::from_reader(reader) { + Ok(contents) => contents, + Err(e) => { + warn!("error serializing file! {}", e); + std::process::exit(exitcode::DATAERR); + } + }; + + info!("succesfully imported bookmarks from {}!", file_path.to_str().unwrap()); + + }, + } +} diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..eb73ba7 --- /dev/null +++ b/src/database.rs @@ -0,0 +1,220 @@ +use crate::structures::Bookmark; +use crate::commands::env_err; + +use paris::*; +use url::Url; +use std::env; +use serde_json::json; +use std::path::PathBuf; + +fn open_database(json: bool, path: Option) -> Option { + let db: sled::Db; + let database_path = match path { + Some(path) => path, + None => { + let key = "HOME"; + match env::var(key) { + Ok(val) => { + let mut tmp_path = PathBuf::new(); + tmp_path.push(val); + tmp_path.push(".local/share/tinymark"); + tmp_path + }, + Err(e) => { + env_err(json, e); + std::process::exit(exitcode::DATAERR); + }, + } + }, + }; + match sled::open(database_path) { + Ok(database) => db = database, + Err(error) => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": error.to_string(), + })); + } else { + error!("error in opening database: {}", error); + } + return None; + }, + }; + + return Some(db); +} + +pub fn insert_entry(entry: &Bookmark, json: bool, path: Option) { + let db = match open_database(json, path) { + Some(database) => database, + None => std::process::exit(exitcode::NOINPUT), + }; + + let bytes; + match bincode::serialize(&entry) { + Ok(result) => bytes = result, + Err(error) => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": error.to_string(), + })); + } else { + error!("failed serializing entry: {}", error); + } + std::process::exit(exitcode::DATAERR); + }, + }; + + match db.insert(entry.link.to_string(), bytes) { + Ok(_) => { + if json { + println!("{}", json!({ + "status": "success", + "reason": "inserted entry", + })); + } else { + info!("succesfully inserted entry {}", entry.link); + } + }, + Err(error) => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": error.to_string(), + })); + } else { + error!("failed to insert entry {}!\n {}", entry.link, error); + } + std::process::exit(exitcode::IOERR); + }, + } + + db.flush().unwrap(); +} + +pub fn remove_entry(link: &Url, json: bool, path: Option) { + let db = match open_database(json, path) { + Some(database) => database, + None => std::process::exit(exitcode::NOINPUT), + }; + + match db.remove(link.to_string()) { + Ok(_) => info!("succesfully removed entry {}", link), + Err(error) => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": error.to_string(), + })); + } else { + error!("failed to remove entry {}!\n {}", link, error); + } + std::process::exit(exitcode::IOERR); + }, + } + + db.flush().unwrap(); +} + +pub fn get_all(json: bool, path: Option) -> Option> { + let db = match open_database(json, path) { + Some(database) => database, + None => return None, + }; + + let first_key = match db.first() { + Ok(pair) => { + pair.unwrap().0 + }, + Err(error) => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": error.to_string(), + })); + } else { + error!("failed to get first key: {}", error); + } + return None; + }, + }; + + let mut bookmarks_vector: Vec = Vec::new(); + let mut iter = db.range(first_key..); + + loop { + match iter.next() { + Some(x) => { + let read_entry; + match bincode::deserialize(&x.unwrap().1) { + Ok(result) => read_entry = result, + Err(error) => { + if json { + println!("{}", json!({ + "status": "fail", + "reason": error.to_string(), + })); + } else { + error!("failed deserializing entry: {}", error); + } + return None; + }, + } + bookmarks_vector.push(read_entry); + }, + None => { break }, + } + } + + return Some(bookmarks_vector); +} + +/* not working for some reason +pub fn update_entry(entry: &Bookmark) { + let db = open_database(); + + let bytes = match bincode::serialize(&entry) { + Ok(bytes) => bytes, + Err(error) => panic!("failed to serialize entry: {}", error), + }; + + let old_entry: Bookmark = get_entry(&entry.id.to_simple().to_string()).unwrap(); + + let old_bytes = match bincode::serialize(&old_entry) { + Ok(bytes) => bytes, + Err(error) => panic!("failed to serialize entry: {}", error), + }; + + match db.compare_and_swap( + entry.id.to_simple().to_string(), + Some(&old_bytes), + Some(&bytes)) { + Ok(_) => info!("succesfully swapped entry {}", entry.id), + Err(error) => warn!("failed to swap entry {}!\n {}", entry.id, error), + } + + db.flush(); +} + +pub fn get_entry(id: &str) -> Option { + let db = open_database(); + + let db_entry = match db.get(id) { + Ok(entry) => entry, + Err(error) => panic!("failed to get old entry: {}", error), + }; + + if let Some(entry) = db_entry { + let read_entry = match bincode::deserialize(&entry) { + Ok(entry) => entry, + Err(error) => panic!("failed to deserialize entry: {}", error), + }; + return Some(read_entry); + } else { + warn!("failed to find entry"); + return None; + }; +} +*/ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b61cba3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,61 @@ +pub mod structures; +mod tests; +mod database; +mod commands; + +use crate::structures::{Commands, Cli, Config}; +use clap::Parser; +use paris::*; +use std::path::PathBuf; +use std::env; + +fn main() { + let cfg: Config = confy::load("tinymark").unwrap(); + confy::store("tinymark", &cfg).unwrap(); + /* + println!("The configuration is:"); + println!("{:#?}", cfg);*/ + + if cfg.tui { + warn!("TUI is not implemented yet!"); + std::process::exit(exitcode::DATAERR); + } + + + let args = Cli::parse(); + let json: bool; + + if cfg.json { + json = true; + } else { + json = args.json; + } + + let storage_path: Option = match &cfg.storage_location { + Some(path) => { + let HOME = env::var("HOME"); + match HOME { + Ok(val) => { + let mut new_path = PathBuf::new(); + new_path.push(val); + new_path.push(path); + Some(new_path) + }, + Err(e) => { + commands::env_err(json, e); + None + }, + } + }, + None => None, + }; + + match &args.command { + Commands::Add { url, name, description, tags } => commands::add(url, name, description, tags, json, storage_path), + Commands::Edit { url } => commands::edit(json, url, storage_path), + Commands::Delete { url } => database::remove_entry(url, json, storage_path), + Commands::List { } => commands::list(json, storage_path), + Commands::Export { file } => commands::export(file.to_path_buf(), json, storage_path), + Commands::Import { file } => commands::import(file.to_path_buf(), json), + } +} diff --git a/src/structures.rs b/src/structures.rs new file mode 100644 index 0000000..a788ed6 --- /dev/null +++ b/src/structures.rs @@ -0,0 +1,131 @@ +use url::Url; +use uuid::Uuid; +use serde_derive::{Serialize, Deserialize}; +use clap::{AppSettings, Parser, Subcommand}; +use std::fmt; +use chrono::prelude::*; +use std::path::PathBuf; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Config { + pub tui: bool, + pub json: bool, + pub storage_location: Option, +} + +impl Default for Config { + fn default() -> Self { + Config { + tui: false, + json: false, + storage_location: Some(PathBuf::from(r"$HOME/.local/share/tinymark")), + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Bookmark { + //pub id: Uuid, + pub link: Url, + pub label: String, + pub description: Option, + pub tags: Vec, + pub container: Option, + pub created_at: DateTime, +} + +fn do_nothing() { + //yea +} + +impl fmt::Display for Bookmark { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + println!("Bookmark: {}", &self.label); + println!("{}", &self.link); + + match &self.description { + Some(s) => println!("{}", s), + None => do_nothing(), + }; + + print!("["); + for i in &self.tags { + print!("{},", i); + } + print!("]"); + + write!(f, "\nCreated at: {}\n", &self.created_at.with_timezone(&Local).to_rfc2822()) + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Container { + pub id: Uuid, + pub label: String, + pub container: Option, + pub container_type: ContainerTypes, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum ContainerTypes { + Folder, + Group, +} + +#[derive(Parser)] +pub struct Cli { + /// Output as JSON + #[clap(long)] + pub json: bool, + + #[clap(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Add a bookmark + #[clap(setting(AppSettings::ArgRequiredElseHelp))] + Add { + /// The URL to add + url: Url, + + /// The name of the bookmark + name: String, + + /// A short description + description: Option, + + /// Optional comma-seperated tags + tags: Vec + }, + + /// Edit a bookmark + Edit { + url: Option, + }, + + /// Delete a bookmark + #[clap(setting(AppSettings::ArgRequiredElseHelp))] + Delete { + // The bookmark to delete + url: Url, + }, + + /// List all bookmarks + List, + + /// Export the bookmarks to a JSON file + #[clap(setting(AppSettings::ArgRequiredElseHelp))] + Export { + /// The output file + file: PathBuf, + }, + + /// Import bookmarks from a JSON file + #[clap(setting(AppSettings::ArgRequiredElseHelp))] + Import { + /// The input file + file: PathBuf, + }, +} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..31e1bb2 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +}