From d17266dc2ec51d01c85e10302facf1f3912b2b52 Mon Sep 17 00:00:00 2001 From: 45Tatami Date: Wed, 24 Apr 2024 18:51:41 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 660 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 15 ++ src/.db.rs.swp | Bin 0 -> 12288 bytes src/db.rs | 199 +++++++++++++++ src/main.rs | 127 ++++++++++ src/models.rs | 30 +++ src/util.rs | 30 +++ src/view.rs | 8 + 9 files changed, 1070 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/.db.rs.swp create mode 100644 src/db.rs create mode 100644 src/main.rs create mode 100644 src/models.rs create mode 100644 src/util.rs create mode 100644 src/view.rs 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..b8b1cdb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,660 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +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 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +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.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rusqlite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[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.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "ttag" +version = "0.1.0" +dependencies = [ + "clap", + "env_logger", + "log", + "md5", + "rusqlite", + "tempfile", + "walkdir", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[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 = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +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-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dfa79e8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ttag" +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.19", features = ["derive"] } +env_logger = "0.10.0" +log = "0.4.20" +md5 = "0.7.0" +rusqlite = { version = "0.30.0", features = ["bundled"] } +tempfile = "3.9.0" +walkdir = "2.4.0" diff --git a/src/.db.rs.swp b/src/.db.rs.swp new file mode 100644 index 0000000000000000000000000000000000000000..fd8774367b1bf78dc2e4fe823859aa3c84f21be3 GIT binary patch literal 12288 zcmeI2&u<$=6vwB%pcYCI2re9WS)_Q4+;x+PUmKFhNnF(;jf?GoR3TaG-LbvV{>sj7 z+7N|s;{bxc00b8faDgiqj@*!t;Km;S2u`SQp%UNOAF)%?f+}$+nw38LYi8cO_kC|h zv9g1UuePqxi^fHU;}OQbrWXIoef{+6B4gX37^jwOxcjj^;=$c}a325vFMt34e4eqNz_;Kt@G1BZd;s1D?|~EyfCXLw z&w>Ro4`u*?C%~6>uJW1Wn!s zV{jGdKm$^%r+_+?0!jg;fKosypcMFDDsXVX2uFUFclEkFlfiFrJ59Jg>!%3`?hOq) z41y}lT5&4-4cx%d=kD;{^+)~4O2&6+*0jGhXsyj|v(uw?hb}c*8`RqDwQ1;j9A8oj z>NYo;YdvzE8m%nQrB3@w)+Xq+%gs)cbXu8Ps#N6Qm`CB#${baxvAIsthozPI^nhRQm*XZq9G4?Qrs)QP$4OsKU_2j^3}*(lRBXgYYM&<>Xn9#z5#%)+MSj`L;Z=)(-b*pA3Tzq!HJ* zM%=Il0-+{6s{p+^8HHq`OI{hYM81{S<9rP5v(hZ;Yds7q3E5W6$sHo29)&T%>LipR zSd7hF_B%OzI&o za53|7vSR+rikV6~JeZ{AgpBLxAda$K$|$v#MT162UGxbopBsEfKK12&HH!~}9WLzH zjS@EuWc$=(b$THb5$EM5g~tq1A*$>jRJ6=kZg*<&oRA6W@Ubjdjw2nN&Ozy-NwXq} zYc%DON$t$jmt@QG35l7cs8j>HT7(a?bD-=FC7M6-7_|!Hb2g)8M zb$2{B;cIzs=wq5($d%=au|Dez`f2P<(z8A&%S0L|}GC`5nVT>$TV06sNf@#&X?G4!g#rJ Db { + + let db_url = env::var("DATABASE_URL").unwrap_or("./db".to_string()); + info!("connecting to '{db_url}'"); + + let mut db = Db { + conn: Connection::open(&db_url) + .expect("could not connect to database") + }; + + db.migrate(); + + db + } + + fn migrate(&mut self) { + let c = &self.conn; + + let code_version = CODE_DB_VERSION; + let mut db_version = 0; + c.pragma_query(None, "user_version", |val| { db_version = val.get(0).unwrap(); Ok(())}).unwrap(); + + info!("code schema version: {code_version}"); + info!("db schema version: {db_version}"); + for (step, migration) in MIGRATIONS.iter().skip(db_version).enumerate() { + let step = step + 1; + + info!("Migrating db step {step}/{code_version:?}"); + c.execute_batch(migration).unwrap(); + info!("done"); + } + } + + pub fn add_tag(&mut self, tag_name: &str, description: Option<&str>) { + info!("inserting new tag '{}'", tag_name); + + let mut stmt = self.conn.prepare_cached( + "INSERT INTO tags (name, description) VALUES (?1, ?2)", + ).unwrap(); + + stmt.execute((&tag_name, &description)).unwrap(); + + info!("success"); + } + + /// Tag image with the given tag + /// + /// Does one lookup for the image and one insertion for the binding. + /// + /// TODO no check for tag and unwraps on insert + pub fn tag_image(&mut self, image: ImgIdent, tag: Tag) { + info!("tagging image"); + + let (repo, path) = match image { + ImgIdent::Rel{repo, path} => (Some(repo), path), + ImgIdent::Abs(p) => (None, p), + ImgIdent::Id(_) => todo!() + + }; + + let repo = repo.as_deref().map(Path::as_os_str).map(OsStr::as_bytes); + let path = path.as_os_str().as_bytes(); + + let mut stmt = self.conn.prepare_cached( + "INSERT INTO filetags (file, tag) SELECT id, ?1 FROM filetags WHERE repo IS ?2 AND path=?3" + ).unwrap(); + + stmt.execute((&tag, repo, path)).unwrap(); + + info!("success"); + } + + pub fn add_files(&mut self, vals: I) + where + I: Iterator + { + let tx = self.conn.transaction().unwrap(); + + // TODO iter parallel + for img in vals { + + let el = match img { + ImgIdent::Rel{repo, path} => { + let mut full_path = repo.clone(); + full_path.push(&path); + let mut file = std::fs::File::open(&full_path).unwrap(); // FIXME uwnrap + let mut data = Vec::::new(); + let _ = file.read_to_end(&mut data); + + let digest = md5::compute(data); + let hash = format!("md5:{:x}", digest); + // let hash = unsafe { String::from_utf8_unchecked(hash.into()) }; + + let metadata = match(std::fs::metadata(full_path)) { + Ok(m) => m, + Err(e) => { + error!("could not get metadata for {path:?}: {e}"); + continue; + } + }; + let mtime = metadata + .modified().unwrap() + .duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap() + .as_secs(); + + let size = metadata.len(); + + (hash, size, repo, path, mtime) + } + _ => todo!() + }; + + let mut stmt = tx.prepare_cached( + "INSERT OR REPLACE INTO files (hash, size, repo, path, mtime) VALUES (?1, ?2, ?3, ?4, ?5)" + ).unwrap(); + + stmt.insert( + (el.0, el.1, el.2.as_os_str().as_bytes(), el.3.as_os_str().as_bytes(), el.4) + ).unwrap(); + } + + tx.commit().unwrap(); + } + + pub fn get_all_files(&self, repo: Option) -> Vec { + let mut stmt = self.conn.prepare_cached( + "SELECT * FROM files WHERE ?1 is NULL OR repo=?1" + ).unwrap(); + + let repo = repo.as_deref().map(Path::as_os_str).map(OsStr::as_bytes); + + stmt.query_map([repo], |row| { + let repo_blob: Vec = row.get_unwrap(3); + let path_blob: Vec = row.get_unwrap(4); + + let repo_osstr = OsString::from_vec(repo_blob); + let path_osstr = OsString::from_vec(path_blob); + + Ok(crate::models::File { + hash: row.get_unwrap(1), + size: row.get_unwrap(2), + repo: Some(repo_osstr.into()), + path: path_osstr.into(), + mtime: row.get_unwrap(5), + }) + }).unwrap().flatten().collect() // FIXME return iterator + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c888e71 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,127 @@ +#![recursion_limit = "1024"] + +mod db; +mod models; +mod util; + +use crate::db::Db; +use crate::util::ImgIdent; + +use clap::{Parser, Subcommand}; + +use walkdir::WalkDir; + +use std::fs::DirEntry; +use std::path::{Path, PathBuf}; + +use env_logger::Env; + +#[derive(Parser)] +struct Cli { + #[command(subcommand)] + command: Commands, + + /// repository to use + #[arg(short, long)] + repo: Option, + + /// interpret paths as repo relative instead of system filepaths + #[arg(long)] + relative: bool +} + +#[derive(Subcommand)] +enum Commands { + /// manage tags + #[command(subcommand)] + Tags(CmdTag), + + /// tag one or more images + Tag { + tag: String, + path: Vec, + }, + + /// Manage repositories + Scan { + path: PathBuf + }, + + List +} + +#[derive(Subcommand)] +enum CmdTag { + Add { + tag: String, + + #[arg(long)] + description: Option, + }, +} + +fn main() { + env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + + let cli = Cli::parse(); + + let mut database = Db::new(); + + match &cli.command { + Commands::Tags(subc) => match subc { + CmdTag::Add {tag, description} => database.add_tag(tag, description.as_deref()) + } + Commands::Tag { tag, path } => { + for img in path { + let img = match cli.repo { + Some(ref repo) => { + let mut path = repo.to_owned(); + path.push("/"); + path.push(img); + path + } + None=> img.to_owned() + }; + + let img = match cli.relative { + true => todo!(), + false => ImgIdent::Abs(img) + }; + + database.tag_image(img, tag.to_string()); + } + } + Commands::Scan { path } => { + // TODO does not handle repo, ImgIdent::TryFrom needs to be changed + // or maybe we can add a filter after conversion and just remove the subpath + let file_it = WalkDir::new(path) + .into_iter() + .inspect(|d| { + if let Err(e) = d { + println!("err: {e}"); + } + }) + .filter_map(Result::ok) + .flat_map(ImgIdent::try_from) + .map(|img| { // inefficient but good enough + match &cli.repo { + Some(repo) => match img { + ImgIdent::Abs(abs) => ImgIdent::Rel{ + repo: repo.to_owned(), + path: abs.strip_prefix(repo).unwrap().to_owned() + }, + _ => panic!("you found a bug") + }, + None => img + } + }); + database.add_files(file_it); + } + Commands::List => { + let files = database.get_all_files(cli.repo); + for f in files { + println!("{f:?}"); + } + } + }; +} diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..32c8820 --- /dev/null +++ b/src/models.rs @@ -0,0 +1,30 @@ +use crate::util::ImgIdent; + +use std::path::PathBuf; + +#[derive(Debug)] +pub struct File { + pub hash: String, + pub size: Option, + pub repo: Option, + pub path: PathBuf, + pub mtime: Option, +} + +pub struct Tag { + pub name: String, + pub description: Option, +} + +pub struct FileTag { + pub file: i32, + pub tag: String, +} + +impl TryFrom<&ImgIdent> for File { + type Error = String; + + fn try_from(i: &ImgIdent) -> Result { + todo!(); + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..9e3db55 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,30 @@ +use walkdir::DirEntry; + +use std::path::{Path, PathBuf}; + +#[derive(Debug)] +pub enum ImgIdent { + Rel {repo: PathBuf, path: PathBuf}, + Abs(PathBuf), + Id(usize) +} + +impl ImgIdent { + pub fn absolute_path(self) -> String { + todo!() + } +} + +impl TryFrom for ImgIdent { + type Error = String; + + fn try_from(value: DirEntry) -> Result { + let file_type = value.file_type(); + if file_type.is_file() { + Ok(ImgIdent::Abs(value.into_path())) + } else { + let path = value.path(); + Err(format!("{path:?} is not file")) + } + } +} diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..d7fef6b --- /dev/null +++ b/src/view.rs @@ -0,0 +1,8 @@ +crate::models::File; + +struct Viewer { +} + +impl Viewer { + pub fn +}