commit b9bf5a831e51200e42a179a3313618bd3a7205a5 Author: Andrew Phillips Date: Tue Jul 25 17:33:50 2023 -0300 Initial commit 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..5303ccb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,715 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[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.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "clap" +version = "4.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is-terminal" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" +dependencies = [ + "hermit-abi 0.3.2", + "rustix", + "windows-sys", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keep-rust" +version = "0.1.0" +dependencies = [ + "clap", + "lazy_static", + "log", + "regex", + "rusqlite", + "rusqlite_migration", + "stderrlog", +] + +[[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.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rusqlite_migration" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef7dd29a4426624704d5966416682fb7ab3682f724986e9e3893eaca44accabc" +dependencies = [ + "log", + "rusqlite", +] + +[[package]] +name = "rustix" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabcb0461ebd01d6b79945797c27f8529082226cb630a9865a71870ff63532a4" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "stderrlog" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69a26bbf6de627d389164afa9783739b56746c6c72c4ed16539f4ff54170327b" +dependencies = [ + "atty", + "chrono", + "log", + "termcolor", + "thread_local", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[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 = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b36b6be --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "keep-rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.3.10", features = ["derive", "env"] } +lazy_static = "1.4.0" +log = "0.4.19" +regex = "1.9.1" +rusqlite = { version = "0.29.0", features = ["bundled"] } +rusqlite_migration = "1.0.2" +stderrlog = "0.5.4" diff --git a/src/compression.rs b/src/compression.rs new file mode 100644 index 0000000..5c2474b --- /dev/null +++ b/src/compression.rs @@ -0,0 +1,98 @@ +use std::fmt; +use std::os::unix::fs::PermissionsExt; +use std::env; +use std::fs; +use log::*; + +#[derive(Debug, Clone)] +pub struct CompressionType { + name: String, + binary: String, + compress: String, + decompress: String, +} + +impl fmt::Display for CompressionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[name='{}', binary='{}', compress='{}', decompress='{}']", self.name, self.binary, self.compress, self.decompress) + } +} + + +pub fn add_compression_type(compression_types: &mut Vec, name: String, binary: String, compress: String, decompress: String) { + let path = is_program_in_path(binary); + + if let Ok(path) = path { + compression_types.push( + CompressionType { + name, + binary: path, + compress, + decompress + }); + } +} + +pub fn supported_compression_types() -> Vec { + let mut compression_types = Vec::new(); + + add_compression_type(&mut compression_types, "lz4".to_string(), "lz4".to_string(), "-qc".to_string() ,"-dc".to_string()); + add_compression_type(&mut compression_types, "gzip".to_string(), "gzip".to_string(), "-qc".to_string() ,"-dc".to_string()); + add_compression_type(&mut compression_types, "bzip2".to_string(), "bzip2".to_string(), "-qc".to_string() ,"-dc".to_string()); + add_compression_type(&mut compression_types, "xz".to_string(), "xz".to_string(), "-qc".to_string() ,"-dc".to_string()); + + compression_types.push( + CompressionType { + name: "none".to_string(), + binary: "".to_string(), + compress: "".to_string(), + decompress: "".to_string(), + }); + + return compression_types; +} + + +pub fn get_compression_default(compression_types: Vec) -> Result { + debug!("Compression type: default"); + + match compression_types.first() { + None => Err(String::from("Unable to find default compression type")), + Some(compression_type) => Ok(compression_type.clone()) + } +} + +pub fn get_compression_named(compression_types: Vec, compression_name: String) -> Result { + debug!("Compression type: {}", compression_name); + match compression_types.iter().find(|&c| c.name == *compression_name) { + None => Err(format!("Unable to find compression type: {}", compression_name)), + Some(compression_type) => Ok(compression_type.clone()) + } +} + +pub fn get_compression(compression_name: Option) -> Result { + let compression_types = supported_compression_types(); + + match compression_name { + None => get_compression_default(compression_types), + Some(compression_name) => get_compression_named(compression_types, compression_name), + } +} + +fn is_program_in_path(program: String) -> Result { + debug!("Looking for executable: {}", program); + if let Ok(path) = env::var("PATH") { + for p in path.split(':') { + let p_str = format!("{}/{}", p, program); + let stat = fs::metadata(p_str.clone()); + if let Ok(stat) = stat { + let md = stat; + let permissions = md.permissions(); + if md.is_file() && permissions.mode() & 0o111 != 0 { + return Ok(p_str); + } + } + } + } + Err(()) +} diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..6f2bae5 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,36 @@ +use rusqlite::{params, Connection, Error}; +use rusqlite_migration::{Migrations, M}; +use log::*; +use lazy_static::lazy_static; + +lazy_static! { + static ref MIGRATIONS: Migrations<'static> = Migrations::new(vec![ + M::up("CREATE TABLE keep( + id INTEGER AUTOINCREMENT NOT NULL, + ts TEXT NOT NULL, + compress TEXT NOT NULL, + hostname TEXT NOT NULL, + comment TEXT NOT NULL) + PRIMARY KEY(id);"), + M::up("CREATE TABLE tags ( + id INTEGER NOT NULL, + name TEXT NOT NULL, + FOREIGN KEY(id) REFERENCES keep(id) ON DELETE CASCADE, + PRIMARY KEY(id, name));") + ]); +} + +fn open(path: String) -> Result { + debug!("Opening DB {}", path); + + match Connection::open(path) { + Ok(mut conn) => match conn.pragma_update(None, "foreign_keys", "ON") { + Ok(()) => match MIGRATIONS.to_latest(&mut conn) { + Ok(()) => Ok(conn), + Err(e) => Err(format!("Error migrating sqlite schema: {}", e)) + }, + Err(e) => Err(format!("Error setting sqlite pragma: {}", e)) + } + Err(e) => Err(format!("Error connecting to database: {}", e)) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1d05954 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,224 @@ +use std::str::FromStr; +use std::path::PathBuf; +use clap::error::ErrorKind; +use clap::*; +use log::*; + +pub mod compression; +pub mod db; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + #[command(flatten)] + mode: ModeArgs, + #[command(flatten)] + item: ItemArgs, + #[command(flatten)] + options: OptionsArgs, + + #[arg()] + ids_or_tags: Vec +} + +#[derive(Parser, Debug)] +struct ModeArgs { + #[arg(group("mode"), help_heading("Mode"), short, long, conflicts_with_all(["get", "list", "update", "delete", "status"]))] + save: bool, + + #[arg(group("mode"), help_heading("Mode"), short, long, conflicts_with_all(["save", "list", "update", "delete", "status"]))] + get: bool, + + #[arg(group("mode"), help_heading("Mode"), short, long, conflicts_with_all(["save", "get", "update", "delete", "status"]))] + list: bool, + + #[arg(group("mode"), help_heading("Mode"), short, long, conflicts_with_all(["save", "get", "list", "delete", "status"]), requires("ids_or_tags"))] + update: bool, + + #[arg(group("mode"), help_heading("Mode"), short, long, conflicts_with_all(["save", "get", "list", "update", "status"]), requires("ids_or_tags"))] + delete: bool, + + #[arg(group("mode"), help_heading("Mode"), short('S'), long, conflicts_with_all(["save", "get", "list", "update", "delete"]))] + status: bool +} + +#[derive(Parser, Debug)] +struct ItemArgs { + #[arg(help_heading("Item"), short, long, conflicts_with("get"), conflicts_with("list"))] + comment: Option, + + #[arg(help_heading("Item"), short('C'), long, conflicts_with("get"), conflicts_with("list"), env("KEEP_COMPRESS"))] + compress: Option, +} + +#[derive(Parser, Debug)] +struct OptionsArgs { + #[arg(help_heading("Options"), long, env("KEEP_DIR"))] + dir: Option, + + #[arg(help_heading("Options"), short, long)] + force: bool, + + #[arg(help_heading("Options"), short, long, action = clap::ArgAction::Count, conflicts_with("quiet"))] + verbose: u8, + + #[arg(help_heading("Options"), short, long)] + quiet: bool, +} + +#[derive(Debug,Clone)] +enum NumberOrString { + Number(u32), + Str(String), +} + +#[derive(Debug,PartialEq)] +enum KeepModes { + Unknown, + Save, + Get, + List, + Update, + Delete, + Status +} + + +impl FromStr for NumberOrString { + type Err = &'static str; // The actual type doesn't matter since we never error, but it must implement `Display` + fn from_str(s: &str) -> Result + { + Ok (s.parse::() + .map(NumberOrString::Number) + .unwrap_or_else(|_| NumberOrString::Str (s.to_string()))) + } +} + + +fn main() { + let mut cmd = Args::command(); + let args = Args::parse(); + + stderrlog::new() + .module(module_path!()) + .quiet(args.options.quiet) + .verbosity(usize::from(args.options.verbose + 2)) + .timestamp(stderrlog::Timestamp::Second) + .init() + .unwrap(); + + debug!("Start"); + + let ids = &mut Vec::new(); + let tags = &mut Vec::new(); + + for v in args.ids_or_tags.iter() { + match v.clone() { + NumberOrString::Number(num) => ids.push(num), + NumberOrString::Str(str) => tags.push(str), + } + } + + let mut mode: KeepModes = KeepModes::Unknown; + if args.mode.save { + mode = KeepModes::Save; + } else if args.mode.get { + mode = KeepModes::Get; + } else if args.mode.list { + mode = KeepModes::List; + } else if args.mode.delete { + mode = KeepModes::Delete; + } else if args.mode.update { + mode = KeepModes::Update; + } else if args.mode.status { + mode = KeepModes::Status; + } + + if mode == KeepModes::Unknown { + if ! ids.is_empty() { + mode = KeepModes::Get; + } else { + mode = KeepModes::Save; + } + } + + debug!("args: {:?}", args); + debug!("ids: {:?}", ids); + debug!("tags: {:?}", tags); + debug!("mode: {:?}", mode); + + match mode { + KeepModes::Save => mode_save(&mut cmd, args, ids, tags), + KeepModes::Get => mode_get(&mut cmd, args, ids, tags), + KeepModes::List => mode_list(&mut cmd, args, ids, tags), + KeepModes::Update => mode_update(&mut cmd, args, ids, tags), + KeepModes::Delete => mode_delete(&mut cmd, args, ids, tags), + KeepModes::Status => mode_status(&mut cmd, args), + _ => todo!() + } +} + + +fn mode_save(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec) { + if ! ids.is_empty() { + cmd.error(ErrorKind::InvalidValue, "ID given, you cannot supply IDs when using --save").exit(); + } + + if tags.is_empty() { + tags.push("none".to_string()); + } + + let compression_type = compression::get_compression(args.item.compress); + +} + + +fn mode_get(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec) { + if ids.is_empty() && tags.is_empty() { + cmd.error(ErrorKind::InvalidValue, "No ID or tags given, ou must supply one ID or atleast one tag when using --get").exit(); + } else if ! ids.is_empty() && ! tags.is_empty() { + cmd.error(ErrorKind::InvalidValue, "Both ID and tags given, you must supply one ID or atleast one tag when using --get").exit(); + } else if ids.len() > 1 { + cmd.error(ErrorKind::InvalidValue, "More than one ID given, you must supply one ID or atleast one tag when using --get").exit(); + } +} + + +fn mode_list(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec) { + if ! ids.is_empty() { + cmd.error(ErrorKind::InvalidValue, "ID given, you can only supply tags when using --list").exit(); + } +} + + +fn mode_update(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec) { + if ids.is_empty() { + cmd.error(ErrorKind::InvalidValue, "No ID given, you must supply one ID when using --update").exit(); + } +} + + +fn mode_delete(cmd: &mut Command, args: Args, ids: &mut Vec, tags: &mut Vec) { + if ids.is_empty() { + cmd.error(ErrorKind::InvalidValue, "No ID given, you must supply one ID when using --delete").exit(); + } else if ! tags.is_empty() { + cmd.error(ErrorKind::InvalidValue, "Tags given, you must supply one ID when using --delete").exit(); + } else if ids.len() > 1 { + cmd.error(ErrorKind::InvalidValue, "More than one ID given, you must supply one ID when using --delete").exit(); + } +} + + +fn mode_status_show_compression() { + let compression_types = compression::supported_compression_types(); + + println!("compression_types:"); + for compression_type in compression_types.into_iter() { + println!(" {}", compression_type); + } +} + + +fn mode_status(cmd: &mut Command, args: Args) { + mode_status_show_compression(); +}